diff --git a/src/json_export.cpp b/src/json_export.cpp index f8f6d3ec1..c9372045b 100644 --- a/src/json_export.cpp +++ b/src/json_export.cpp @@ -1,5 +1,7 @@ #include "behaviortree_cpp/json_export.h" +#include + namespace BT { @@ -72,9 +74,27 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source) return Entry{ BT::Any(source.get()), BT::TypeInfo::Create() }; } + // get() silently wraps any value outside the int range, so keep the + // width when the number doesn't fit. This lets an int64_t/uint64_t entry + // survive an export/import round-trip instead of coming back truncated. + if(source.is_number_unsigned()) + { + const uint64_t value = source.get(); + if(value <= static_cast(std::numeric_limits::max())) + { + return Entry{ BT::Any(static_cast(value)), BT::TypeInfo::Create() }; + } + return Entry{ BT::Any(value), BT::TypeInfo::Create() }; + } if(source.is_number_integer()) { - return Entry{ BT::Any(source.get()), BT::TypeInfo::Create() }; + const int64_t value = source.get(); + if(value >= static_cast(std::numeric_limits::min()) && + value <= static_cast(std::numeric_limits::max())) + { + return Entry{ BT::Any(static_cast(value)), BT::TypeInfo::Create() }; + } + return Entry{ BT::Any(value), BT::TypeInfo::Create() }; } if(source.is_number_float()) { diff --git a/tests/gtest_json.cpp b/tests/gtest_json.cpp index 3f0eced39..559b405e8 100644 --- a/tests/gtest_json.cpp +++ b/tests/gtest_json.cpp @@ -210,6 +210,42 @@ TEST_F(JsonTest, BlackboardInOut) ASSERT_EQ(vect_out.z, 3.3); } +TEST_F(JsonTest, LargeIntegerPreserved) +{ + BT::JsonExporter& exporter = BT::JsonExporter::get(); + + // Values outside the int32 range used to be silently wrapped by get(). + { + auto json = nlohmann::json::parse(R"({"a": 2147483648, "b": 5000000000, + "c": 4294967296})"); + auto bb = BT::Blackboard::create(); + ImportBlackboardFromJSON(json, *bb); + ASSERT_EQ(bb->get("a"), 2147483648LL); + ASSERT_EQ(bb->get("b"), 5000000000LL); + ASSERT_EQ(bb->get("c"), 4294967296LL); + } + // Negative values below the int32 minimum are preserved too. + { + auto res = exporter.fromJson(nlohmann::json(int64_t(-5000000000LL))); + ASSERT_TRUE(res) << res.error(); + ASSERT_EQ(res->first.cast(), -5000000000LL); + } + // Values that fit keep the int type, unchanged from before. + { + auto res = exporter.fromJson(nlohmann::json(100)); + ASSERT_TRUE(res) << res.error(); + ASSERT_EQ(res->first.cast(), 100); + } + // An int64_t entry survives an Export/Import round-trip. + { + auto bb_in = BT::Blackboard::create(); + bb_in->set("big", int64_t(5000000000LL)); + auto bb_out = BT::Blackboard::create(); + ImportBlackboardFromJSON(ExportBlackboardToJSON(*bb_in), *bb_out); + ASSERT_EQ(bb_out->get("big"), 5000000000LL); + } +} + TEST_F(JsonTest, VectorInteger) { BT::JsonExporter& exporter = BT::JsonExporter::get();