Skip to content

Commit

Permalink
Add type & value fields to primitive json types
Browse files Browse the repository at this point in the history
This aligns with how custom types are represented by the JsonExporter.

* Update both toJson & fromJson functions in the JsonExporter
* Add & update tests

Co-authored by: David Sobek <[email protected]>
  • Loading branch information
dyackzan committed Jan 29, 2025
1 parent 48f6c5b commit c5561ac
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 31 deletions.
70 changes: 44 additions & 26 deletions src/json_export.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#include "behaviortree_cpp/json_export.h"

namespace
{
constexpr std::string_view kTypeField = "__type";
constexpr std::string_view kValueField = "value";
} // namespace

namespace BT
{

Expand All @@ -16,19 +22,23 @@ bool JsonExporter::toJson(const Any& any, nlohmann::json& dst) const

if(any.isString())
{
dst = any.cast<std::string>();
dst[kTypeField] = "string";
dst[kValueField] = any.cast<std::string>();
}
else if(type == typeid(int64_t))
{
dst = any.cast<int64_t>();
dst[kTypeField] = "int64_t";
dst[kValueField] = any.cast<int64_t>();
}
else if(type == typeid(uint64_t))
{
dst = any.cast<uint64_t>();
dst[kTypeField] = "uint64_t";
dst[kValueField] = any.cast<uint64_t>();
}
else if(type == typeid(double))
{
dst = any.cast<double>();
dst[kTypeField] = "double";
dst[kValueField] = any.cast<double>();
}
else
{
Expand All @@ -51,33 +61,41 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source)
{
return nonstd::make_unexpected("json object is null");
}
if(source.is_string())
{
return Entry{ BT::Any(source.get<std::string>()),
BT::TypeInfo::Create<std::string>() };
}
if(source.is_number_unsigned())
{
return Entry{ BT::Any(source.get<uint64_t>()), BT::TypeInfo::Create<uint64_t>() };
}
if(source.is_number_integer())
if(const auto source_type_it = source.find(kTypeField); source_type_it == source.end())
{
return Entry{ BT::Any(source.get<int64_t>()), BT::TypeInfo::Create<int64_t>() };
}
if(source.is_number_float())
{
return Entry{ BT::Any(source.get<double>()), BT::TypeInfo::Create<double>() };
}
if(source.is_boolean())
{
return Entry{ BT::Any(source.get<bool>()), BT::TypeInfo::Create<bool>() };
return nonstd::make_unexpected("Missing field '" + std::string(kTypeField) + "'.");
}

if(!source.contains("__type"))
const auto source_value_it = source.find(kValueField);
if(source_value_it != source.end())
{
return nonstd::make_unexpected("Missing field '__type'");
if(source_value_it->is_string())
{
return Entry{ BT::Any(source_value_it->get<std::string>()),
BT::TypeInfo::Create<std::string>() };
}
if(source_value_it->is_number_unsigned())
{
return Entry{ BT::Any(source_value_it->get<uint64_t>()),
BT::TypeInfo::Create<uint64_t>() };
}
if(source_value_it->is_number_integer())
{
return Entry{ BT::Any(source_value_it->get<int64_t>()),
BT::TypeInfo::Create<int64_t>() };
}
if(source_value_it->is_number_float())
{
return Entry{ BT::Any(source_value_it->get<double>()),
BT::TypeInfo::Create<double>() };
}
if(source_value_it->is_boolean())
{
return Entry{ BT::Any(source_value_it->get<bool>()), BT::TypeInfo::Create<bool>() };
}
}
auto type_it = type_names_.find(source["__type"]);

auto type_it = type_names_.find(source[kTypeField]);
if(type_it == type_names_.end())
{
return nonstd::make_unexpected("Type not found in registered list");
Expand Down
31 changes: 26 additions & 5 deletions tests/gtest_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
#include "behaviortree_cpp/json_export.h"
#include "behaviortree_cpp/basic_types.h"

namespace
{
constexpr std::string_view kTypeField = "__type";
constexpr std::string_view kValueField = "value";
} // namespace

//----------- Custom types ----------

namespace TestTypes
Expand Down Expand Up @@ -95,16 +101,27 @@ TEST_F(JsonTest, TwoWaysConversion)
TestTypes::Pose3D pose = { { 1, 2, 3 }, { 4, 5, 6, 7 } };

nlohmann::json json;
exporter.toJson(BT::Any("string_val"), json["string"]);
exporter.toJson(BT::Any(69), json["int"]);
exporter.toJson(BT::Any(static_cast<uint64_t>(96)), json["uint"]);
exporter.toJson(BT::Any(3.14), json["real"]);
exporter.toJson(BT::Any(pose), json["pose"]);

std::cout << json.dump(2) << std::endl;

ASSERT_EQ(json["int"], 69);
ASSERT_EQ(json["real"], 3.14);
ASSERT_EQ(json["string"][kTypeField], "string");
ASSERT_EQ(json["string"][kValueField], "string_val");

ASSERT_EQ(json["int"][kTypeField], "int64_t");
ASSERT_EQ(json["int"][kValueField], 69);

ASSERT_EQ(json["uint"][kTypeField], "uint64_t");
ASSERT_EQ(json["uint"][kValueField], 96);

ASSERT_EQ(json["real"][kTypeField], "double");
ASSERT_EQ(json["real"][kValueField], 3.14);

ASSERT_EQ(json["pose"]["__type"], "Pose3D");
ASSERT_EQ(json["pose"][kTypeField], "Pose3D");
ASSERT_EQ(json["pose"]["pos"]["x"], 1);
ASSERT_EQ(json["pose"]["pos"]["y"], 2);
ASSERT_EQ(json["pose"]["pos"]["z"], 3);
Expand All @@ -126,8 +143,12 @@ TEST_F(JsonTest, TwoWaysConversion)
ASSERT_EQ(pose.rot.y, pose2.rot.y);
ASSERT_EQ(pose.rot.z, pose2.rot.z);

auto num = exporter.fromJson(json["int"])->first.cast<int>();
ASSERT_EQ(num, 69);
auto string_val = exporter.fromJson(json["string"])->first.cast<std::string>();
ASSERT_EQ(string_val, "string_val");
auto int_val = exporter.fromJson(json["int"])->first.cast<int>();
ASSERT_EQ(int_val, 69);
auto uint_val = exporter.fromJson(json["uint"])->first.cast<uint>();
ASSERT_EQ(uint_val, 96);
auto real = exporter.fromJson(json["real"])->first.cast<double>();
ASSERT_EQ(real, 3.14);
}
Expand Down

0 comments on commit c5561ac

Please sign in to comment.