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