BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
json_export.h
1#pragma once
2
3#include "behaviortree_cpp/basic_types.h"
4#include "behaviortree_cpp/utils/safe_any.hpp"
5
6// Use the version of nlohmann::json embedded in BT.CPP,
7// or the system one if BT.CPP was built with USE_VENDORED_JSON=OFF
8#ifdef BTCPP_SYSTEM_JSON
9#include <nlohmann/json.hpp>
10#else
11#include "behaviortree_cpp/contrib/json.hpp"
12#endif
13
14namespace BT
15{
16
17/**
18* To add new type to the JSON library, you should follow these instructions:
19* https://json.nlohmann.me/features/arbitrary_types/
20*
21* Considering for instance the type:
22*
23* struct Point2D {
24* double x;
25* double y;
26* };
27*
28* This would require the implementation of:
29*
30* void to_json(nlohmann::json& j, const Point2D& point);
31* void from_json(const nlohmann::json& j, Point2D& point);
32*
33* To avoid repeating yourself, we provide the macro BT_JSON_CONVERTION
34* that implements both those function, at once. Usage:
35*
36* BT_JSON_CONVERTER(Point2D, point)
37* {
38* add_field("x", &point.x);
39* add_field("y", &point.y);
40* }
41*
42* Later, you MUST register the type using:
43*
44* BT::RegisterJsonDefinition<Point2D>();
45*/
46
47//-----------------------------------------------------------------------------------
48
49/**
50* Use RegisterJsonDefinition<Foo>();
51*/
52
53class JsonExporter
54{
55public:
56 static JsonExporter& get();
57
58 ~JsonExporter() = default;
59
60 JsonExporter(const JsonExporter&) = delete;
61 JsonExporter& operator=(const JsonExporter&) = delete;
62 JsonExporter(JsonExporter&&) = delete;
63 JsonExporter& operator=(JsonExporter&&) = delete;
64
65private:
66 JsonExporter() = default;
67
68public:
69 /**
70 * @brief toJson adds the content of "any" to the JSON "destination".
71 *
72 * It will return false if the conversion toJson is not possible
73 * If it is a custom type, you might register it first with addConverter().
74 */
75 bool toJson(const BT::Any& any, nlohmann::json& destination) const;
76
77 /// This information is needed to create a BT::Blackboard::entry
78 using Entry = std::pair<BT::Any, BT::TypeInfo>;
79
80 using ExpectedEntry = nonstd::expected<Entry, std::string>;
81
82 /**
83 * @brief fromJson will return an Entry (value wrappedn in Any + TypeInfo)
84 * from a json source.
85 * If it is a custom type, you might register it first with addConverter().
86 * @param source
87 * @return
88 */
89 ExpectedEntry fromJson(const nlohmann::json& source) const;
90
91 /// Same as the other, but providing the specific type,
92 /// To be preferred if the JSON doesn't contain the field [__type]
93 ExpectedEntry fromJson(const nlohmann::json& source, std::type_index type) const;
94
95 template <typename T>
96 Expected<T> fromJson(const nlohmann::json& source) const;
97
98 /**
99 * @brief Register new JSON converters with addConverter<Foo>().
100 * You should used first the macro BT_JSON_CONVERTER.
101 * The conversions from/to vector<T> are automatically registered.
102 */
103 template <typename T>
104 void addConverter();
105
106 /**
107 * @brief addConverter register a to_json function that converts a json to a type T.
108 * The conversion to std:vector<T> is automatically registered.
109 *
110 * @param to_json the function with signature void(const T&, nlohmann::json&)
111 * @param add_type if true, add a field called [__type] with the name of the type.
112 */
113 template <typename T>
114 void addConverter(std::function<void(const T&, nlohmann::json&)> to_json,
115 bool add_type = true);
116
117 /**
118 * @brief addConverter register a from_json function that converts a json to a type T.
119 * The conversions from std::vector<T> is automatically registered.
120 *
121 * @param from_json the function with signature void(const nlohmann::json&, T&)
122 */
123 template <typename T>
124 void addConverter(std::function<void(const nlohmann::json&, T&)> from_json);
125
126private:
127 using ToJonConverter = std::function<void(const BT::Any&, nlohmann::json&)>;
128 using FromJonConverter = std::function<Entry(const nlohmann::json&)>;
129
130 std::unordered_map<std::type_index, ToJonConverter> to_json_converters_;
131 std::unordered_map<std::type_index, FromJonConverter> from_json_converters_;
132 std::unordered_map<std::type_index, FromJonConverter> from_json_array_converters_;
133 std::unordered_map<std::string, BT::TypeInfo> type_names_;
134};
135
136template <typename T>
137inline Expected<T> JsonExporter::fromJson(const nlohmann::json& source) const
138{
139 auto res = fromJson(source);
140 if(!res)
141 {
142 return nonstd::make_unexpected(res.error());
143 }
144 auto casted = res->first.tryCast<T>();
145 if(!casted)
146 {
147 return nonstd::make_unexpected(casted.error());
148 }
149 return *casted;
150}
151
152//-------------------------------------------------------------------
153
154template <typename T>
155inline void JsonExporter::addConverter()
156{
157 // we need to get the name of the type
158 nlohmann::json const js = T{};
159 // we insert both the name obtained from JSON and demangle
160 if(js.contains("__type"))
161 {
162 type_names_.insert({ std::string(js["__type"]), BT::TypeInfo::Create<T>() });
163 }
164 type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
165
166 ToJonConverter to_converter = [](const BT::Any& entry, nlohmann::json& dst) {
167 dst = *const_cast<BT::Any&>(entry).castPtr<T>();
168 };
169 to_json_converters_.insert({ typeid(T), to_converter });
170
171 FromJonConverter from_converter = [](const nlohmann::json& src) -> Entry {
172 T value = src.get<T>();
173 return { BT::Any(value), BT::TypeInfo::Create<T>() };
174 };
175
176 from_json_converters_.insert({ typeid(T), from_converter });
177
178 //---- include vectors of T
179 ToJonConverter to_array_converter = [](const BT::Any& entry, nlohmann::json& dst) {
180 dst = *const_cast<BT::Any&>(entry).castPtr<std::vector<T>>();
181 };
182 to_json_converters_.insert({ typeid(std::vector<T>), to_array_converter });
183
184 FromJonConverter from_array_converter = [](const nlohmann::json& src) -> Entry {
185 std::vector<T> value;
186 for(const auto& item : src)
187 {
188 value.push_back(item.get<T>());
189 }
190 return { BT::Any(value), BT::TypeInfo::Create<std::vector<T>>() };
191 };
192 from_json_array_converters_.insert({ typeid(T), from_array_converter });
193}
194
195template <typename T>
196inline void JsonExporter::addConverter(
197 std::function<void(const T&, nlohmann::json&)> func, bool add_type)
198{
199 auto converter = [func, add_type](const BT::Any& entry, nlohmann::json& json) {
200 func(entry.cast<T>(), json);
201 if(add_type)
202 {
203 json["__type"] = BT::demangle(typeid(T));
204 }
205 };
206 //---------------------------------------------
207 // add the vector<T> converter (must be created before moving converter)
208 auto vector_converter = [converter](const BT::Any& entry, nlohmann::json& json) {
209 auto& vec = *const_cast<BT::Any&>(entry).castPtr<std::vector<T>>();
210 for(const auto& item : vec)
211 {
212 nlohmann::json item_json;
213 converter(BT::Any(item), item_json);
214 json.push_back(item_json);
215 }
216 };
217 to_json_converters_.insert({ typeid(T), std::move(converter) });
218 to_json_converters_.insert({ typeid(std::vector<T>), std::move(vector_converter) });
219}
220
221template <typename T>
222inline void
223JsonExporter::addConverter(std::function<void(const nlohmann::json&, T&)> func)
224{
225 auto converter = [func](const nlohmann::json& json) -> Entry {
226 T tmp;
227 func(json, tmp);
228 return { BT::Any(tmp), BT::TypeInfo::Create<T>() };
229 };
230 type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
231 from_json_converters_.insert({ typeid(T), std::move(converter) });
232 //---------------------------------------------
233 // add the vector<T> converter
234 auto vector_converter = [func](const nlohmann::json& json) -> Entry {
235 std::vector<T> tmp;
236 for(const auto& item : json)
237 {
238 T item_tmp;
239 func(item, item_tmp);
240 tmp.push_back(item_tmp);
241 }
242 return { BT::Any(tmp), BT::TypeInfo::Create<std::vector<T>>() };
243 };
244 from_json_array_converters_.insert({ typeid(T), std::move(vector_converter) });
245}
246
247template <typename T>
248inline void RegisterJsonDefinition()
249{
250 JsonExporter::get().addConverter<T>();
251}
252
253} // namespace BT
254
255//------------------------------------------------
256//------------------------------------------------
257//------------------------------------------------
258
259// Macro to implement to_json() and from_json()
260// NOLINTBEGIN(bugprone-macro-parentheses)
261#define BT_JSON_CONVERTER(Type, value)
262 template <class AddField>
263 void _JsonTypeDefinition(Type&, AddField&);
264
265 inline void to_json(nlohmann::json& js, const Type& p)
266 {
267 auto op = [&js](const char* name, auto* val) { js[name] = *val; };
268 _JsonTypeDefinition(const_cast<Type&>(p), op);
269 js["__type"] = #Type;
270 }
271
272 inline void from_json(const nlohmann::json& js, Type& p)
273 {
274 auto op = [&js](const char* name, auto* v) { js.at(name).get_to(*v); };
275 _JsonTypeDefinition(p, op);
276 }
277
278 template <class AddField>
279 inline void _JsonTypeDefinition(Type& value, AddField& add_field)
280// NOLINTEND(bugprone-macro-parentheses)
281
282//end of file
Definition: safe_any.hpp:57
Definition: json_export.h:54
ExpectedEntry fromJson(const nlohmann::json &source, std::type_index type) const
void addConverter(std::function< void(const T &, nlohmann::json &)> to_json, bool add_type=true)
addConverter register a to_json function that converts a json to a type T. The conversion to std:vect...
Definition: json_export.h:196
void addConverter(std::function< void(const nlohmann::json &, T &)> from_json)
addConverter register a from_json function that converts a json to a type T. The conversions from std...
Definition: json_export.h:223
bool toJson(const BT::Any &any, nlohmann::json &destination) const
toJson adds the content of "any" to the JSON "destination".
void addConverter()
Register new JSON converters with addConverter<Foo>(). You should used first the macro BT_JSON_CONVER...
Definition: json_export.h:155
ExpectedEntry fromJson(const nlohmann::json &source) const
fromJson will return an Entry (value wrappedn in Any + TypeInfo) from a json source....
Definition: basic_types.h:377
Definition: action_node.h:24