Add deserializeXxx(JsonVariant, ...) (resolves #1226)

This commit is contained in:
Benoit Blanchon
2023-07-29 05:07:20 +02:00
parent bc8ea36781
commit 43eed00cd9
8 changed files with 249 additions and 38 deletions

View File

@ -24,3 +24,4 @@ HEAD
* Show a link to the documentation when user passes an unsupported input type * Show a link to the documentation when user passes an unsupported input type
* Remove `JsonDocument::memoryUsage()` * Remove `JsonDocument::memoryUsage()`
* Remove `JsonDocument::garbageCollect()` * Remove `JsonDocument::garbageCollect()`
* Add `deserializeJson(JsonVariant, ...)` and `deserializeMsgPack(JsonVariant, ...)` (#1226)

View File

@ -5,6 +5,7 @@
add_executable(JsonDeserializerTests add_executable(JsonDeserializerTests
array.cpp array.cpp
DeserializationError.cpp DeserializationError.cpp
destination_types.cpp
errors.cpp errors.cpp
filter.cpp filter.cpp
input_types.cpp input_types.cpp

View File

@ -0,0 +1,104 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Allocators.hpp"
TEST_CASE("deserializeJson(JsonDocument&)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
auto err = deserializeJson(doc, "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[42]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofPool()),
Deallocate(sizeofString("hello")),
Allocate(sizeofPool()),
});
}
TEST_CASE("deserializeJson(JsonVariant)") {
SECTION("variant is bound") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
JsonVariant variant = doc[0];
auto err = deserializeJson(variant, "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("variant is unbound") {
JsonVariant variant;
auto err = deserializeJson(variant, "[42]");
REQUIRE(err == DeserializationError::NoMemory);
}
}
TEST_CASE("deserializeJson(ElementProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
SECTION("element already exists") {
auto err = deserializeJson(doc[0], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("element must be created exists") {
auto err = deserializeJson(doc[1], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[\"hello\",[42]]");
REQUIRE(spy.log() == AllocatorLog{});
}
}
TEST_CASE("deserializeJson(MemberProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc[std::string("hello")] = std::string("world");
spy.clearLog();
SECTION("member already exists") {
auto err = deserializeJson(doc["hello"], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("member must be created exists") {
auto err = deserializeJson(doc["value"], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
REQUIRE(spy.log() == AllocatorLog{});
}
}

View File

@ -6,6 +6,7 @@ add_executable(MsgPackDeserializerTests
deserializeArray.cpp deserializeArray.cpp
deserializeObject.cpp deserializeObject.cpp
deserializeVariant.cpp deserializeVariant.cpp
destination_types.cpp
doubleToFloat.cpp doubleToFloat.cpp
errors.cpp errors.cpp
filter.cpp filter.cpp

View File

@ -0,0 +1,104 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Allocators.hpp"
TEST_CASE("deserializeMsgPack(JsonDocument&)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
auto err = deserializeMsgPack(doc, "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[42]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofPool()),
Deallocate(sizeofString("hello")),
Allocate(sizeofPool()),
});
}
TEST_CASE("deserializeMsgPack(JsonVariant)") {
SECTION("variant is bound") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
JsonVariant variant = doc[0];
auto err = deserializeMsgPack(variant, "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("variant is unbound") {
JsonVariant variant;
auto err = deserializeMsgPack(variant, "\x91\x2A");
REQUIRE(err == DeserializationError::NoMemory);
}
}
TEST_CASE("deserializeMsgPack(ElementProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
spy.clearLog();
SECTION("element already exists") {
auto err = deserializeMsgPack(doc[0], "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("element must be created exists") {
auto err = deserializeMsgPack(doc[1], "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[\"hello\",[42]]");
REQUIRE(spy.log() == AllocatorLog{});
}
}
TEST_CASE("deserializeMsgPack(MemberProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc[std::string("hello")] = std::string("world");
spy.clearLog();
SECTION("member already exists") {
auto err = deserializeMsgPack(doc["hello"], "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("member must be created exists") {
auto err = deserializeMsgPack(doc["value"], "\x91\x2A");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
REQUIRE(spy.log() == AllocatorLog{});
}
}

View File

@ -22,40 +22,37 @@ struct first_or_void<T, Rest...> {
using type = T; using type = T;
}; };
template <template <typename> class TDeserializer, typename TReader> template <template <typename> class TDeserializer, typename TDestination,
TDeserializer<TReader> makeDeserializer(ResourceManager* resources, typename TReader, typename TOptions>
TReader reader) { DeserializationError doDeserialize(TDestination&& dst, TReader reader,
ARDUINOJSON_ASSERT(resources != 0); TOptions options) {
return TDeserializer<TReader>(resources, reader); auto data = VariantAttorney::getOrCreateData(dst);
if (!data)
return DeserializationError::NoMemory;
auto resources = VariantAttorney::getResourceManager(dst);
dst.clear();
return TDeserializer<TReader>(resources, reader)
.parse(*data, options.filter, options.nestingLimit);
} }
template <template <typename> class TDeserializer, typename TStream, template <template <typename> class TDeserializer, typename TDestination,
typename... Args, typename TStream, typename... Args,
typename = typename enable_if< // issue #1897 typename = typename enable_if< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value>::type> !is_integral<typename first_or_void<Args...>::type>::value>::type>
DeserializationError deserialize(JsonDocument& doc, TStream&& input, DeserializationError deserialize(TDestination&& dst, TStream&& input,
Args... args) { Args... args) {
auto reader = makeReader(detail::forward<TStream>(input)); return doDeserialize<TDeserializer>(
auto data = VariantAttorney::getData(doc); dst, makeReader(detail::forward<TStream>(input)),
auto resources = VariantAttorney::getResourceManager(doc); makeDeserializationOptions(args...));
auto options = makeDeserializationOptions(args...);
doc.clear();
return makeDeserializer<TDeserializer>(resources, reader)
.parse(*data, options.filter, options.nestingLimit);
} }
template <template <typename> class TDeserializer, typename TChar, template <template <typename> class TDeserializer, typename TDestination,
typename Size, typename... Args, typename TChar, typename Size, typename... Args,
typename = typename enable_if<is_integral<Size>::value>::type> typename = typename enable_if<is_integral<Size>::value>::type>
DeserializationError deserialize(JsonDocument& doc, TChar* input, DeserializationError deserialize(TDestination&& dst, TChar* input,
Size inputSize, Args... args) { Size inputSize, Args... args) {
auto reader = makeReader(input, size_t(inputSize)); return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)),
auto data = VariantAttorney::getData(doc); makeDeserializationOptions(args...));
auto resources = VariantAttorney::getResourceManager(doc);
auto options = makeDeserializationOptions(args...);
doc.clear();
return makeDeserializer<TDeserializer>(resources, reader)
.parse(*data, options.filter, options.nestingLimit);
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -670,20 +670,21 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v6/api/json/deserializejson/ // https://arduinojson.org/v6/api/json/deserializejson/
template <typename... Args> template <typename TDestination, typename... Args>
DeserializationError deserializeJson(JsonDocument& doc, Args&&... args) { DeserializationError deserializeJson(TDestination&& dst, Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(doc, detail::forward<Args>(args)...); return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...);
} }
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v6/api/json/deserializejson/ // https://arduinojson.org/v6/api/json/deserializejson/
template <typename TChar, typename... Args> template <typename TDestination, typename TChar, typename... Args>
DeserializationError deserializeJson(JsonDocument& doc, TChar* input, DeserializationError deserializeJson(TDestination&& dst, TChar* input,
Args&&... args) { Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(doc, input, return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...); input, detail::forward<Args>(args)...);
} }
ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -560,19 +560,21 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a MessagePack input and puts the result in a JsonDocument. // Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/ // https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
template <typename... Args> template <typename TDestination, typename... Args>
DeserializationError deserializeMsgPack(JsonDocument& doc, Args&&... args) { DeserializationError deserializeMsgPack(TDestination&& dst, Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<MsgPackDeserializer>(doc, detail::forward<Args>(args)...); return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...);
} }
// Parses a MessagePack input and puts the result in a JsonDocument. // Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/ // https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
template <typename TChar, typename... Args> template <typename TDestination, typename TChar, typename... Args>
DeserializationError deserializeMsgPack(JsonDocument& doc, TChar* input, DeserializationError deserializeMsgPack(TDestination&& dst, TChar* input,
Args&&... args) { Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<MsgPackDeserializer>(doc, input, return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
input,
detail::forward<Args>(args)...); detail::forward<Args>(args)...);
} }