Skip to content

Commit 52d6d8d

Browse files
committed
fix issue #829: support again custom JSON converters
1 parent 38a15d3 commit 52d6d8d

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

include/behaviortree_cpp/json_export.h

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include "behaviortree_cpp/basic_types.h"
44
#include "behaviortree_cpp/utils/safe_any.hpp"
5-
#include "behaviortree_cpp/contrib/expected.hpp"
5+
#include "behaviortree_cpp/basic_types.h"
66

77
// Use the version nlohmann::json embedded in BT.CPP
88
#include "behaviortree_cpp/contrib/json.hpp"
@@ -73,14 +73,32 @@ class JsonExporter
7373
*/
7474
ExpectedEntry fromJson(const nlohmann::json& source) const;
7575

76-
/// Same as the other, but providing the specific type
76+
/// Same as the other, but providing the specific type,
77+
/// To be preferred if the JSON doesn't contain the field [__type]
7778
ExpectedEntry fromJson(const nlohmann::json& source, std::type_index type) const;
7879

80+
template <typename T>
81+
Expected<T> fromJson(const nlohmann::json& source) const;
82+
7983
/// Register new JSON converters with addConverter<Foo>().
8084
/// You should have used first the macro BT_JSON_CONVERTER
8185
template <typename T>
8286
void addConverter();
8387

88+
/**
89+
* @brief addConverter register a to_json function that converts a json to a type T.
90+
*
91+
* @param to_json the function with signature void(const T&, nlohmann::json&)
92+
* @param add_type if true, add a field called [__type] with the name ofthe type.
93+
* */
94+
template <typename T>
95+
void addConverter(std::function<void(const T&, nlohmann::json&)> to_json,
96+
bool add_type = true);
97+
98+
/// Register custom from_json converter directly.
99+
template <typename T>
100+
void addConverter(std::function<void(const nlohmann::json&, T&)> from_json);
101+
84102
private:
85103
using ToJonConverter = std::function<void(const BT::Any&, nlohmann::json&)>;
86104
using FromJonConverter = std::function<Entry(const nlohmann::json&)>;
@@ -90,6 +108,22 @@ class JsonExporter
90108
std::unordered_map<std::string, BT::TypeInfo> type_names_;
91109
};
92110

111+
template <typename T>
112+
inline Expected<T> JsonExporter::fromJson(const nlohmann::json& source) const
113+
{
114+
auto res = fromJson(source);
115+
if(!res)
116+
{
117+
return nonstd::expected_lite::make_unexpected(res.error());
118+
}
119+
auto casted = res->first.tryCast<T>();
120+
if(!casted)
121+
{
122+
return nonstd::expected_lite::make_unexpected(casted.error());
123+
}
124+
return *casted;
125+
}
126+
93127
//-------------------------------------------------------------------
94128

95129
template <typename T>
@@ -117,6 +151,33 @@ inline void JsonExporter::addConverter()
117151
from_json_converters_.insert({ typeid(T), from_converter });
118152
}
119153

154+
template <typename T>
155+
inline void JsonExporter::addConverter(
156+
std::function<void(const T&, nlohmann::json&)> func, bool add_type)
157+
{
158+
auto converter = [func, add_type](const BT::Any& entry, nlohmann::json& json) {
159+
func(entry.cast<T>(), json);
160+
if(add_type)
161+
{
162+
json["__type"] = BT::demangle(typeid(T));
163+
}
164+
};
165+
to_json_converters_.insert({ typeid(T), std::move(converter) });
166+
}
167+
168+
template <typename T>
169+
inline void
170+
JsonExporter::addConverter(std::function<void(const nlohmann::json&, T&)> func)
171+
{
172+
auto converter = [func](const nlohmann::json& json) -> Entry {
173+
T tmp;
174+
func(json, tmp);
175+
return { BT::Any(tmp), BT::TypeInfo::Create<T>() };
176+
};
177+
type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
178+
from_json_converters_.insert({ typeid(T), std::move(converter) });
179+
}
180+
120181
template <typename T>
121182
inline void RegisterJsonDefinition()
122183
{

tests/gtest_json.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ struct Pose3D
2929
Quaternion3D rot;
3030
};
3131

32+
struct Time
33+
{
34+
uint32_t sec;
35+
uint32_t nsec;
36+
};
37+
3238
BT_JSON_CONVERTER(Vector3D, v)
3339
{
3440
add_field("x", &v.x);
@@ -50,6 +56,19 @@ BT_JSON_CONVERTER(Pose3D, v)
5056
add_field("rot", &v.rot);
5157
}
5258

59+
// specialized functions
60+
void jsonFromTime(const Time& t, nlohmann::json& j)
61+
{
62+
j["stamp"] = double(t.sec) + 1e-9 * double(t.nsec);
63+
}
64+
65+
void jsonToTime(const nlohmann::json& j, Time& t)
66+
{
67+
double sec = j["stamp"];
68+
t.sec = int(sec);
69+
t.nsec = (sec - t.sec) * 1e9;
70+
}
71+
5372
} // namespace TestTypes
5473

5574
//----------- JSON specialization ----------
@@ -63,6 +82,9 @@ class JsonTest : public testing::Test
6382
exporter.addConverter<TestTypes::Pose3D>();
6483
exporter.addConverter<TestTypes::Vector3D>();
6584
exporter.addConverter<TestTypes::Quaternion3D>();
85+
86+
exporter.addConverter<TestTypes::Time>(TestTypes::jsonFromTime);
87+
exporter.addConverter<TestTypes::Time>(TestTypes::jsonToTime);
6688
}
6789
};
6890

@@ -110,6 +132,37 @@ TEST_F(JsonTest, TwoWaysConversion)
110132
ASSERT_EQ(real, 3.14);
111133
}
112134

135+
TEST_F(JsonTest, CustomTime)
136+
{
137+
BT::JsonExporter& exporter = BT::JsonExporter::get();
138+
139+
TestTypes::Time stamp = { 3, 8000000 };
140+
nlohmann::json json;
141+
exporter.toJson(BT::Any(stamp), json);
142+
std::cout << json.dump() << std::endl;
143+
144+
{
145+
auto res = exporter.fromJson(json, typeid(TestTypes::Time));
146+
ASSERT_TRUE(res);
147+
auto stamp_out = res->first.cast<TestTypes::Time>();
148+
ASSERT_EQ(stamp.sec, stamp_out.sec);
149+
ASSERT_EQ(stamp.nsec, stamp_out.nsec);
150+
}
151+
{
152+
auto res = exporter.fromJson(json);
153+
ASSERT_TRUE(res);
154+
auto stamp_out = res->first.cast<TestTypes::Time>();
155+
ASSERT_EQ(stamp.sec, stamp_out.sec);
156+
ASSERT_EQ(stamp.nsec, stamp_out.nsec);
157+
}
158+
{
159+
auto stamp_out = exporter.fromJson<TestTypes::Time>(json);
160+
ASSERT_TRUE(stamp_out);
161+
ASSERT_EQ(stamp.sec, stamp_out->sec);
162+
ASSERT_EQ(stamp.nsec, stamp_out->nsec);
163+
}
164+
}
165+
113166
TEST_F(JsonTest, ConvertFromString)
114167
{
115168
TestTypes::Vector3D vect;

0 commit comments

Comments
 (0)