Skip to content

Commit 0ee319b

Browse files
extend JSON convertion to include vectors (#965)
* extend JSOn convertion to include vectors * fixing comments
1 parent ac26aea commit 0ee319b

File tree

5 files changed

+245
-65
lines changed

5 files changed

+245
-65
lines changed

include/behaviortree_cpp/json_export.h

+65-14
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace BT
1111
{
1212

1313
/**
14-
* To add new type to the JSON library, you should follow these isntructions:
14+
* To add new type to the JSON library, you should follow these instructions:
1515
* https://json.nlohmann.me/features/arbitrary_types/
1616
*
1717
* Considering for instance the type:
@@ -80,22 +80,31 @@ class JsonExporter
8080
template <typename T>
8181
Expected<T> fromJson(const nlohmann::json& source) const;
8282

83-
/// Register new JSON converters with addConverter<Foo>().
84-
/// You should have used first the macro BT_JSON_CONVERTER
83+
/**
84+
* @brief Register new JSON converters with addConverter<Foo>().
85+
* You should used first the macro BT_JSON_CONVERTER.
86+
* The convertions from/to vector<T> are automatically registered.
87+
*/
8588
template <typename T>
8689
void addConverter();
8790

8891
/**
8992
* @brief addConverter register a to_json function that converts a json to a type T.
93+
* The convertion to std:vector<T> is automatically registered.
9094
*
9195
* @param to_json the function with signature void(const T&, nlohmann::json&)
9296
* @param add_type if true, add a field called [__type] with the name ofthe type.
93-
* */
97+
*/
9498
template <typename T>
9599
void addConverter(std::function<void(const T&, nlohmann::json&)> to_json,
96100
bool add_type = true);
97101

98-
/// Register custom from_json converter directly.
102+
/**
103+
* @brief addConverter register a from_json function that converts a json to a type T.
104+
* The convertions from std::vector<T> is automatically registered.
105+
*
106+
* @param from_json the function with signature void(const nlohmann::json&, T&)
107+
*/
99108
template <typename T>
100109
void addConverter(std::function<void(const nlohmann::json&, T&)> from_json);
101110

@@ -105,6 +114,7 @@ class JsonExporter
105114

106115
std::unordered_map<std::type_index, ToJonConverter> to_json_converters_;
107116
std::unordered_map<std::type_index, FromJonConverter> from_json_converters_;
117+
std::unordered_map<std::type_index, FromJonConverter> from_json_array_converters_;
108118
std::unordered_map<std::string, BT::TypeInfo> type_names_;
109119
};
110120

@@ -129,6 +139,15 @@ inline Expected<T> JsonExporter::fromJson(const nlohmann::json& source) const
129139
template <typename T>
130140
inline void JsonExporter::addConverter()
131141
{
142+
// we need to get the name of the type
143+
nlohmann::json const js = T{};
144+
// we insert both the name obtained from JSON and demangle
145+
if(js.contains("__type"))
146+
{
147+
type_names_.insert({ std::string(js["__type"]), BT::TypeInfo::Create<T>() });
148+
}
149+
type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
150+
132151
ToJonConverter to_converter = [](const BT::Any& entry, nlohmann::json& dst) {
133152
dst = *const_cast<BT::Any&>(entry).castPtr<T>();
134153
};
@@ -139,16 +158,23 @@ inline void JsonExporter::addConverter()
139158
return { BT::Any(value), BT::TypeInfo::Create<T>() };
140159
};
141160

142-
// we need to get the name of the type
143-
nlohmann::json const js = T{};
144-
// we insert both the name obtained from JSON and demangle
145-
if(js.contains("__type"))
146-
{
147-
type_names_.insert({ std::string(js["__type"]), BT::TypeInfo::Create<T>() });
148-
}
149-
type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
150-
151161
from_json_converters_.insert({ typeid(T), from_converter });
162+
163+
//---- include vectors of T
164+
ToJonConverter to_array_converter = [](const BT::Any& entry, nlohmann::json& dst) {
165+
dst = *const_cast<BT::Any&>(entry).castPtr<std::vector<T>>();
166+
};
167+
to_json_converters_.insert({ typeid(std::vector<T>), to_array_converter });
168+
169+
FromJonConverter from_array_converter = [](const nlohmann::json& src) -> Entry {
170+
std::vector<T> value;
171+
for(const auto& item : src)
172+
{
173+
value.push_back(item.get<T>());
174+
}
175+
return { BT::Any(value), BT::TypeInfo::Create<std::vector<T>>() };
176+
};
177+
from_json_array_converters_.insert({ typeid(T), from_array_converter });
152178
}
153179

154180
template <typename T>
@@ -163,6 +189,18 @@ inline void JsonExporter::addConverter(
163189
}
164190
};
165191
to_json_converters_.insert({ typeid(T), std::move(converter) });
192+
//---------------------------------------------
193+
// add the vector<T> converter
194+
auto vector_converter = [converter](const BT::Any& entry, nlohmann::json& json) {
195+
auto& vec = *const_cast<BT::Any&>(entry).castPtr<std::vector<T>>();
196+
for(const auto& item : vec)
197+
{
198+
nlohmann::json item_json;
199+
converter(BT::Any(item), item_json);
200+
json.push_back(item_json);
201+
}
202+
};
203+
to_json_converters_.insert({ typeid(std::vector<T>), std::move(vector_converter) });
166204
}
167205

168206
template <typename T>
@@ -176,6 +214,19 @@ JsonExporter::addConverter(std::function<void(const nlohmann::json&, T&)> func)
176214
};
177215
type_names_.insert({ BT::demangle(typeid(T)), BT::TypeInfo::Create<T>() });
178216
from_json_converters_.insert({ typeid(T), std::move(converter) });
217+
//---------------------------------------------
218+
// add the vector<T> converter
219+
auto vector_converter = [func](const nlohmann::json& json) -> Entry {
220+
std::vector<T> tmp;
221+
for(const auto& item : json)
222+
{
223+
T item_tmp;
224+
func(item, item_tmp);
225+
tmp.push_back(item_tmp);
226+
}
227+
return { BT::Any(tmp), BT::TypeInfo::Create<std::vector<T>>() };
228+
};
229+
from_json_array_converters_.insert({ typeid(T), std::move(vector_converter) });
179230
}
180231

181232
template <typename T>

include/behaviortree_cpp/utils/safe_any.hpp

+1-26
Original file line numberDiff line numberDiff line change
@@ -162,32 +162,7 @@ class Any
162162
[[nodiscard]] T* castPtr()
163163
{
164164
static_assert(!std::is_same_v<T, float>, "The value has been casted internally to "
165-
"[double]. "
166-
"Use that instead");
167-
static_assert(!SafeAny::details::is_integer<T>() || std::is_same_v<T, uint64_t>, "The"
168-
" va"
169-
"lue"
170-
" ha"
171-
"s "
172-
"bee"
173-
"n "
174-
"cas"
175-
"ted"
176-
" in"
177-
"ter"
178-
"nal"
179-
"ly "
180-
"to "
181-
"[in"
182-
"t64"
183-
"_t]"
184-
". "
185-
"Use"
186-
" th"
187-
"at "
188-
"ins"
189-
"tea"
190-
"d");
165+
"[double]. Use that instead");
191166

192167
return _any.empty() ? nullptr : linb::any_cast<T>(&_any);
193168
}

src/bt_factory.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <filesystem>
1414
#include "behaviortree_cpp/bt_factory.h"
1515
#include "behaviortree_cpp/utils/shared_library.h"
16-
#include "behaviortree_cpp/contrib/json.hpp"
1716
#include "behaviortree_cpp/xml_parsing.h"
1817
#include "wildcards/wildcards.hpp"
1918

src/json_export.cpp

+54-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ bool JsonExporter::toJson(const Any& any, nlohmann::json& dst) const
3030
{
3131
dst = any.cast<double>();
3232
}
33+
else if(type == typeid(std::vector<double>))
34+
{
35+
dst = any.cast<std::vector<double>>();
36+
}
37+
else if(type == typeid(std::vector<int>))
38+
{
39+
dst = any.cast<std::vector<int>>();
40+
}
41+
else if(type == typeid(std::vector<std::string>))
42+
{
43+
dst = any.cast<std::vector<std::string>>();
44+
}
45+
else if(type == typeid(std::vector<bool>))
46+
{
47+
dst = any.cast<std::vector<bool>>();
48+
}
3349
else
3450
{
3551
auto it = to_json_converters_.find(type);
@@ -49,20 +65,17 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source)
4965
{
5066
if(source.is_null())
5167
{
52-
return nonstd::make_unexpected("json object is null");
68+
return Entry{ BT::Any(), BT::TypeInfo::Create<std::nullptr_t>() };
5369
}
70+
5471
if(source.is_string())
5572
{
5673
return Entry{ BT::Any(source.get<std::string>()),
5774
BT::TypeInfo::Create<std::string>() };
5875
}
59-
if(source.is_number_unsigned())
60-
{
61-
return Entry{ BT::Any(source.get<uint64_t>()), BT::TypeInfo::Create<uint64_t>() };
62-
}
6376
if(source.is_number_integer())
6477
{
65-
return Entry{ BT::Any(source.get<int64_t>()), BT::TypeInfo::Create<int64_t>() };
78+
return Entry{ BT::Any(source.get<int>()), BT::TypeInfo::Create<int>() };
6679
}
6780
if(source.is_number_float())
6881
{
@@ -73,17 +86,48 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source)
7386
return Entry{ BT::Any(source.get<bool>()), BT::TypeInfo::Create<bool>() };
7487
}
7588

76-
if(!source.contains("__type"))
89+
// basic vectors
90+
if(source.is_array() && source.size() > 0 && !source.contains("__type"))
91+
{
92+
auto first_element = source[0];
93+
if(first_element.is_string())
94+
{
95+
return Entry{ BT::Any(source.get<std::vector<std::string>>()),
96+
BT::TypeInfo::Create<std::vector<std::string>>() };
97+
}
98+
if(first_element.is_number_integer())
99+
{
100+
return Entry{ BT::Any(source.get<std::vector<int>>()),
101+
BT::TypeInfo::Create<std::vector<int>>() };
102+
}
103+
if(first_element.is_number_float())
104+
{
105+
return Entry{ BT::Any(source.get<std::vector<double>>()),
106+
BT::TypeInfo::Create<std::vector<double>>() };
107+
}
108+
if(first_element.is_boolean())
109+
{
110+
return Entry{ BT::Any(source.get<std::vector<bool>>()),
111+
BT::TypeInfo::Create<std::vector<bool>>() };
112+
}
113+
}
114+
115+
if(!source.contains("__type") && !source.is_array())
77116
{
78117
return nonstd::make_unexpected("Missing field '__type'");
79118
}
80-
auto type_it = type_names_.find(source["__type"]);
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);
81125
if(type_it == type_names_.end())
82126
{
83127
return nonstd::make_unexpected("Type not found in registered list");
84128
}
85-
auto func_it = from_json_converters_.find(type_it->second.type());
86-
if(func_it == from_json_converters_.end())
129+
auto func_it = from_converters.find(type_it->second.type());
130+
if(func_it == from_converters.end())
87131
{
88132
return nonstd::make_unexpected("Type not found in registered list");
89133
}

0 commit comments

Comments
 (0)