Skip to content

Commit 8bb73ee

Browse files
dyackzanshaur-k
authored andcommitted
Add type & value fields to primitive json types (#5)
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]>
1 parent 6a36881 commit 8bb73ee

File tree

2 files changed

+74
-56
lines changed

2 files changed

+74
-56
lines changed

src/json_export.cpp

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#include "behaviortree_cpp/json_export.h"
22

3+
namespace
4+
{
5+
constexpr std::string_view kTypeField = "__type";
6+
constexpr std::string_view kValueField = "value";
7+
} // namespace
8+
39
namespace BT
410
{
511

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

1723
if(any.isString())
1824
{
19-
dst = any.cast<std::string>();
25+
dst[kTypeField] = "string";
26+
dst[kValueField] = any.cast<std::string>();
2027
}
2128
else if(type == typeid(int64_t))
2229
{
23-
dst = any.cast<int64_t>();
30+
dst[kTypeField] = "int64_t";
31+
dst[kValueField] = any.cast<int64_t>();
2432
}
2533
else if(type == typeid(uint64_t))
2634
{
27-
dst = any.cast<uint64_t>();
35+
dst[kTypeField] = "uint64_t";
36+
dst[kValueField] = any.cast<uint64_t>();
2837
}
2938
else if(type == typeid(double))
3039
{
31-
dst = any.cast<double>();
40+
dst[kTypeField] = "double";
41+
dst[kValueField] = any.cast<double>();
3242
}
3343
else if(type == typeid(std::vector<double>))
3444
{
@@ -67,61 +77,41 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source)
6777
{
6878
return Entry{ BT::Any(), BT::TypeInfo::Create<std::nullptr_t>() };
6979
}
70-
71-
if(source.is_string())
72-
{
73-
return Entry{ BT::Any(source.get<std::string>()),
74-
BT::TypeInfo::Create<std::string>() };
75-
}
76-
if(source.is_number_integer())
77-
{
78-
return Entry{ BT::Any(source.get<int>()), BT::TypeInfo::Create<int>() };
79-
}
80-
if(source.is_number_float())
80+
if(!source.contains(kTypeField))
8181
{
82-
return Entry{ BT::Any(source.get<double>()), BT::TypeInfo::Create<double>() };
83-
}
84-
if(source.is_boolean())
85-
{
86-
return Entry{ BT::Any(source.get<bool>()), BT::TypeInfo::Create<bool>() };
82+
return nonstd::make_unexpected("Missing field '" + std::string(kTypeField) + "'.");
8783
}
8884

89-
// basic vectors
90-
if(source.is_array() && source.size() > 0 && !source.contains("__type"))
85+
const auto source_value_it = source.find(kValueField);
86+
if(source_value_it != source.end())
9187
{
92-
auto first_element = source[0];
93-
if(first_element.is_string())
88+
if(source_value_it->is_string())
9489
{
95-
return Entry{ BT::Any(source.get<std::vector<std::string>>()),
96-
BT::TypeInfo::Create<std::vector<std::string>>() };
90+
return Entry{ BT::Any(source_value_it->get<std::string>()),
91+
BT::TypeInfo::Create<std::string>() };
9792
}
98-
if(first_element.is_number_integer())
93+
if(source_value_it->is_number_unsigned())
9994
{
100-
return Entry{ BT::Any(source.get<std::vector<int>>()),
101-
BT::TypeInfo::Create<std::vector<int>>() };
95+
return Entry{ BT::Any(source_value_it->get<uint64_t>()),
96+
BT::TypeInfo::Create<uint64_t>() };
10297
}
103-
if(first_element.is_number_float())
98+
if(source_value_it->is_number_integer())
10499
{
105-
return Entry{ BT::Any(source.get<std::vector<double>>()),
106-
BT::TypeInfo::Create<std::vector<double>>() };
100+
return Entry{ BT::Any(source_value_it->get<int64_t>()),
101+
BT::TypeInfo::Create<int64_t>() };
107102
}
108-
if(first_element.is_boolean())
103+
if(source_value_it->is_number_float())
109104
{
110-
return Entry{ BT::Any(source.get<std::vector<bool>>()),
111-
BT::TypeInfo::Create<std::vector<bool>>() };
105+
return Entry{ BT::Any(source_value_it->get<double>()),
106+
BT::TypeInfo::Create<double>() };
107+
}
108+
if(source_value_it->is_boolean())
109+
{
110+
return Entry{ BT::Any(source_value_it->get<bool>()), BT::TypeInfo::Create<bool>() };
112111
}
113112
}
114113

115-
if(!source.contains("__type") && !source.is_array())
116-
{
117-
return nonstd::make_unexpected("Missing field '__type'");
118-
}
119-
120-
auto& from_converters =
121-
source.is_array() ? from_json_array_converters_ : from_json_converters_;
122-
auto type_field = source.is_array() ? source[0]["__type"] : source["__type"];
123-
124-
auto type_it = type_names_.find(type_field);
114+
auto type_it = type_names_.find(source[kTypeField]);
125115
if(type_it == type_names_.end())
126116
{
127117
return nonstd::make_unexpected("Type not found in registered list");

tests/gtest_json.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
#include "behaviortree_cpp/json_export.h"
44
#include "behaviortree_cpp/basic_types.h"
55

6+
namespace
7+
{
8+
constexpr std::string_view kTypeField = "__type";
9+
constexpr std::string_view kValueField = "value";
10+
} // namespace
11+
612
//----------- Custom types ----------
713

814
namespace TestTypes
@@ -107,16 +113,27 @@ TEST_F(JsonTest, TwoWaysConversion)
107113
TestTypes::Pose3D pose = { { 1, 2, 3 }, { 4, 5, 6, 7 } };
108114

109115
nlohmann::json json;
116+
exporter.toJson(BT::Any("string_val"), json["string"]);
110117
exporter.toJson(BT::Any(69), json["int"]);
118+
exporter.toJson(BT::Any(static_cast<uint64_t>(96)), json["uint"]);
111119
exporter.toJson(BT::Any(3.14), json["real"]);
112120
exporter.toJson(BT::Any(pose), json["pose"]);
113121

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

116-
ASSERT_EQ(json["int"], 69);
117-
ASSERT_EQ(json["real"], 3.14);
124+
ASSERT_EQ(json["string"][kTypeField], "string");
125+
ASSERT_EQ(json["string"][kValueField], "string_val");
126+
127+
ASSERT_EQ(json["int"][kTypeField], "int64_t");
128+
ASSERT_EQ(json["int"][kValueField], 69);
129+
130+
ASSERT_EQ(json["uint"][kTypeField], "uint64_t");
131+
ASSERT_EQ(json["uint"][kValueField], 96);
118132

119-
ASSERT_EQ(json["pose"]["__type"], "Pose3D");
133+
ASSERT_EQ(json["real"][kTypeField], "double");
134+
ASSERT_EQ(json["real"][kValueField], 3.14);
135+
136+
ASSERT_EQ(json["pose"][kTypeField], "Pose3D");
120137
ASSERT_EQ(json["pose"]["pos"]["x"], 1);
121138
ASSERT_EQ(json["pose"]["pos"]["y"], 2);
122139
ASSERT_EQ(json["pose"]["pos"]["z"], 3);
@@ -126,14 +143,25 @@ TEST_F(JsonTest, TwoWaysConversion)
126143
ASSERT_EQ(json["pose"]["rot"]["y"], 6);
127144
ASSERT_EQ(json["pose"]["rot"]["z"], 7);
128145

129-
auto num_result = exporter.fromJson(json["int"]);
130-
ASSERT_TRUE(num_result) << num_result.error();
131-
auto num = num_result->first.cast<int>();
132-
ASSERT_EQ(num, 69);
133-
134-
auto real_result = exporter.fromJson(json["real"]);
135-
ASSERT_TRUE(real_result) << real_result.error();
136-
auto real = real_result->first.cast<double>();
146+
// check the two-ways transform, i.e. "from_json"
147+
auto pose2 = exporter.fromJson(json["pose"])->first.cast<TestTypes::Pose3D>();
148+
149+
ASSERT_EQ(pose.pos.x, pose2.pos.x);
150+
ASSERT_EQ(pose.pos.y, pose2.pos.y);
151+
ASSERT_EQ(pose.pos.z, pose2.pos.z);
152+
153+
ASSERT_EQ(pose.rot.w, pose2.rot.w);
154+
ASSERT_EQ(pose.rot.x, pose2.rot.x);
155+
ASSERT_EQ(pose.rot.y, pose2.rot.y);
156+
ASSERT_EQ(pose.rot.z, pose2.rot.z);
157+
158+
auto string_val = exporter.fromJson(json["string"])->first.cast<std::string>();
159+
ASSERT_EQ(string_val, "string_val");
160+
auto int_val = exporter.fromJson(json["int"])->first.cast<int>();
161+
ASSERT_EQ(int_val, 69);
162+
auto uint_val = exporter.fromJson(json["uint"])->first.cast<uint>();
163+
ASSERT_EQ(uint_val, 96);
164+
auto real = exporter.fromJson(json["real"])->first.cast<double>();
137165
ASSERT_EQ(real, 3.14);
138166

139167
auto pose_result = exporter.fromJson(json["pose"]);

0 commit comments

Comments
 (0)