فهرست منبع

Replaced JsonDocument::nestingLimit with a param to deserializeJson()

Benoit Blanchon 7 سال پیش
والد
کامیت
e633292df1

+ 21 - 0
CHANGELOG.md

@@ -12,9 +12,13 @@ HEAD
 * `JsonDocument` was missing in the ArduinoJson namespace
 * Added `memoryUsage()` to `JsonArray`, `JsonObject`, and `JsonVariant`
 * Added `nesting()` to `JsonArray`, `JsonDocument`, `JsonObject`, and `JsonVariant`
+* Replaced `JsonDocument::nestingLimit` with an additional parameter
+  to `deserializeJson()` and `deserializeMsgPack()`
 
 > ### BREAKING CHANGES
 > 
+> #### `DynamicJsonDocument`'s constructor
+> 
 > The parameter to the constructor of `DynamicJsonDocument` is now mandatory
 >
 > Old code:
@@ -28,6 +32,23 @@ HEAD
 > ```c++
 > DynamicJsonDocument doc(1024);
 > ```
+> 
+> #### Nesting limit
+> 
+> `JsonDocument::nestingLimit` was replaced with a new parameter to `deserializeJson()` and `deserializeMsgPack()`.
+> 
+> Old code:
+> 
+> ```c++
+> doc.nestingLimit = 15;
+> deserializeJson(doc, input);
+> ```
+> 
+> New code: 
+> 
+> ```c++
+> deserializeJson(doc, input, DeserializationOption::NestingLimit(15));
+> ```
 
 v6.7.0-beta (2018-12-07)
 -----------

+ 4 - 0
src/ArduinoJson.hpp

@@ -49,4 +49,8 @@ using ARDUINOJSON_NAMESPACE::serializeJson;
 using ARDUINOJSON_NAMESPACE::serializeJsonPretty;
 using ARDUINOJSON_NAMESPACE::serializeMsgPack;
 using ARDUINOJSON_NAMESPACE::StaticJsonDocument;
+
+namespace DeserializationOption {
+using ARDUINOJSON_NAMESPACE::NestingLimit;
+}
 }  // namespace ArduinoJson

+ 17 - 0
src/ArduinoJson/Deserialization/NestingLimit.hpp

@@ -0,0 +1,17 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+
+namespace ARDUINOJSON_NAMESPACE {
+
+struct NestingLimit {
+  NestingLimit() : value(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
+  explicit NestingLimit(uint8_t n) : value(n) {}
+
+  uint8_t value;
+};
+}  // namespace ARDUINOJSON_NAMESPACE

+ 18 - 14
src/ArduinoJson/Deserialization/deserialize.hpp

@@ -5,12 +5,13 @@
 #pragma once
 
 #include "../StringStorage/StringStorage.hpp"
-#include "./ArduinoStreamReader.hpp"
-#include "./CharPointerReader.hpp"
-#include "./DeserializationError.hpp"
-#include "./FlashStringReader.hpp"
-#include "./IteratorReader.hpp"
-#include "./StdStreamReader.hpp"
+#include "ArduinoStreamReader.hpp"
+#include "CharPointerReader.hpp"
+#include "DeserializationError.hpp"
+#include "FlashStringReader.hpp"
+#include "IteratorReader.hpp"
+#include "NestingLimit.hpp"
+#include "StdStreamReader.hpp"
 
 namespace ARDUINOJSON_NAMESPACE {
 
@@ -26,22 +27,24 @@ TDeserializer<TReader, TWriter> makeDeserializer(MemoryPool &pool,
 // TString = const std::string&, const String&
 template <template <typename, typename> class TDeserializer, typename TString>
 typename enable_if<!is_array<TString>::value, DeserializationError>::type
-deserialize(JsonDocument &doc, const TString &input) {
+deserialize(JsonDocument &doc, const TString &input,
+            NestingLimit nestingLimit) {
   doc.clear();
   return makeDeserializer<TDeserializer>(
              doc.memoryPool(), makeReader(input),
-             makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
+             makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
       .parse(doc.data());
 }
 //
 // DeserializationError deserialize(JsonDocument& doc, TChar* input);
 // TChar* = char*, const char*, const __FlashStringHelper*
 template <template <typename, typename> class TDeserializer, typename TChar>
-DeserializationError deserialize(JsonDocument &doc, TChar *input) {
+DeserializationError deserialize(JsonDocument &doc, TChar *input,
+                                 NestingLimit nestingLimit) {
   doc.clear();
   return makeDeserializer<TDeserializer>(
              doc.memoryPool(), makeReader(input),
-             makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
+             makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
       .parse(doc.data());
 }
 //
@@ -50,22 +53,23 @@ DeserializationError deserialize(JsonDocument &doc, TChar *input) {
 // TChar* = char*, const char*, const __FlashStringHelper*
 template <template <typename, typename> class TDeserializer, typename TChar>
 DeserializationError deserialize(JsonDocument &doc, TChar *input,
-                                 size_t inputSize) {
+                                 size_t inputSize, NestingLimit nestingLimit) {
   doc.clear();
   return makeDeserializer<TDeserializer>(
              doc.memoryPool(), makeReader(input, inputSize),
-             makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
+             makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
       .parse(doc.data());
 }
 //
 // DeserializationError deserialize(JsonDocument& doc, TStream input);
 // TStream = std::istream&, Stream&
 template <template <typename, typename> class TDeserializer, typename TStream>
-DeserializationError deserialize(JsonDocument &doc, TStream &input) {
+DeserializationError deserialize(JsonDocument &doc, TStream &input,
+                                 NestingLimit nestingLimit) {
   doc.clear();
   return makeDeserializer<TDeserializer>(
              doc.memoryPool(), makeReader(input),
-             makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
+             makeStringStorage(doc.memoryPool(), input), nestingLimit.value)
       .parse(doc.data());
 }
 }  // namespace ARDUINOJSON_NAMESPACE

+ 2 - 7
src/ArduinoJson/Document/JsonDocument.hpp

@@ -12,8 +12,6 @@ namespace ARDUINOJSON_NAMESPACE {
 
 class JsonDocument : public Visitable {
  public:
-  uint8_t nestingLimit;
-
   template <typename Visitor>
   void accept(Visitor& visitor) const {
     return getVariant().accept(visitor);
@@ -67,14 +65,11 @@ class JsonDocument : public Visitable {
   }
 
  protected:
-  JsonDocument(MemoryPool pool)
-      : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT), _pool(pool) {}
+  JsonDocument(MemoryPool pool) : _pool(pool) {}
 
-  JsonDocument(char* buf, size_t capa)
-      : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT), _pool(buf, capa) {}
+  JsonDocument(char* buf, size_t capa) : _pool(buf, capa) {}
 
   void copy(const JsonDocument& src) {
-    nestingLimit = src.nestingLimit;
     to<VariantRef>().set(src.as<VariantRef>());
   }
 

+ 16 - 9
src/ArduinoJson/Json/JsonDeserializer.hpp

@@ -337,23 +337,30 @@ class JsonDeserializer {
 };
 
 template <typename TInput>
-DeserializationError deserializeJson(JsonDocument &doc, const TInput &input) {
-  return deserialize<JsonDeserializer>(doc, input);
+DeserializationError deserializeJson(
+    JsonDocument &doc, const TInput &input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<JsonDeserializer>(doc, input, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeJson(JsonDocument &doc, TInput *input) {
-  return deserialize<JsonDeserializer>(doc, input);
+DeserializationError deserializeJson(
+    JsonDocument &doc, TInput *input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<JsonDeserializer>(doc, input, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeJson(JsonDocument &doc, TInput *input,
-                                     size_t inputSize) {
-  return deserialize<JsonDeserializer>(doc, input, inputSize);
+DeserializationError deserializeJson(
+    JsonDocument &doc, TInput *input, size_t inputSize,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeJson(JsonDocument &doc, TInput &input) {
-  return deserialize<JsonDeserializer>(doc, input);
+DeserializationError deserializeJson(
+    JsonDocument &doc, TInput &input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<JsonDeserializer>(doc, input, nestingLimit);
 }
 }  // namespace ARDUINOJSON_NAMESPACE

+ 16 - 10
src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp

@@ -327,24 +327,30 @@ class MsgPackDeserializer {
 };
 
 template <typename TInput>
-DeserializationError deserializeMsgPack(JsonDocument &doc,
-                                        const TInput &input) {
-  return deserialize<MsgPackDeserializer>(doc, input);
+DeserializationError deserializeMsgPack(
+    JsonDocument &doc, const TInput &input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeMsgPack(JsonDocument &doc, TInput *input) {
-  return deserialize<MsgPackDeserializer>(doc, input);
+DeserializationError deserializeMsgPack(
+    JsonDocument &doc, TInput *input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeMsgPack(JsonDocument &doc, TInput *input,
-                                        size_t inputSize) {
-  return deserialize<MsgPackDeserializer>(doc, input, inputSize);
+DeserializationError deserializeMsgPack(
+    JsonDocument &doc, TInput *input, size_t inputSize,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit);
 }
 
 template <typename TInput>
-DeserializationError deserializeMsgPack(JsonDocument &doc, TInput &input) {
-  return deserialize<MsgPackDeserializer>(doc, input);
+DeserializationError deserializeMsgPack(
+    JsonDocument &doc, TInput &input,
+    NestingLimit nestingLimit = NestingLimit()) {
+  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit);
 }
 }  // namespace ARDUINOJSON_NAMESPACE

+ 84 - 18
test/JsonDeserializer/nestingLimit.cpp

@@ -9,27 +9,93 @@
 #define SHOULD_FAIL(expression) \
   REQUIRE(DeserializationError::TooDeep == expression);
 
-TEST_CASE("JsonDeserializer nestingLimit") {
+TEST_CASE("JsonDeserializer nesting") {
   DynamicJsonDocument doc(4096);
 
-  SECTION("limit = 0") {
-    doc.nestingLimit = 0;
-    SHOULD_WORK(deserializeJson(doc, "\"toto\""));
-    SHOULD_WORK(deserializeJson(doc, "123"));
-    SHOULD_WORK(deserializeJson(doc, "true"));
-    SHOULD_FAIL(deserializeJson(doc, "[]"));
-    SHOULD_FAIL(deserializeJson(doc, "{}"));
-    SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]"));
-    SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}"));
+  SECTION("Input = const char*") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeJson(doc, "\"toto\"", nesting));
+      SHOULD_WORK(deserializeJson(doc, "123", nesting));
+      SHOULD_WORK(deserializeJson(doc, "true", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[]", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{}", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", nesting));
+      SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", nesting));
+    }
+  }
+
+  SECTION("char* and size_t") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeJson(doc, "\"toto\"", 6, nesting));
+      SHOULD_WORK(deserializeJson(doc, "123", 3, nesting));
+      SHOULD_WORK(deserializeJson(doc, "true", 4, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[]", 2, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{}", 2, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", 8, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", 8, nesting));
+      SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", 11, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", 11, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", 10, nesting));
+      SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", 12, nesting));
+    }
   }
 
-  SECTION("limit = 1") {
-    doc.nestingLimit = 1;
-    SHOULD_WORK(deserializeJson(doc, "[\"toto\"]"));
-    SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}"));
-    SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}"));
-    SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}"));
-    SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]"));
-    SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]"));
+  SECTION("Input = std::string") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeJson(doc, std::string("\"toto\""), nesting));
+      SHOULD_WORK(deserializeJson(doc, std::string("123"), nesting));
+      SHOULD_WORK(deserializeJson(doc, std::string("true"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("[]"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("{}"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("[\"toto\"]"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":1}"), nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeJson(doc, std::string("[\"toto\"]"), nesting));
+      SHOULD_WORK(deserializeJson(doc, std::string("{\"toto\":1}"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":{}}"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":[]}"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("[[\"toto\"]]"), nesting));
+      SHOULD_FAIL(deserializeJson(doc, std::string("[{\"toto\":1}]"), nesting));
+    }
+  }
+
+  SECTION("Input = std::istream") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      std::istringstream good("true");
+      std::istringstream bad("[]");
+      SHOULD_WORK(deserializeJson(doc, good, nesting));
+      SHOULD_FAIL(deserializeJson(doc, bad, nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      std::istringstream good("[\"toto\"]");
+      std::istringstream bad("{\"toto\":{}}");
+      SHOULD_WORK(deserializeJson(doc, good, nesting));
+      SHOULD_FAIL(deserializeJson(doc, bad, nesting));
+    }
   }
 }

+ 0 - 10
test/JsonDocument/DynamicJsonDocument.cpp

@@ -78,7 +78,6 @@ TEST_CASE("DynamicJsonDocument copies") {
   SECTION("Copy constructor") {
     DynamicJsonDocument doc1(1234);
     deserializeJson(doc1, "{\"hello\":\"world\"}");
-    doc1.nestingLimit = 42;
 
     DynamicJsonDocument doc2 = doc1;
 
@@ -86,14 +85,12 @@ TEST_CASE("DynamicJsonDocument copies") {
     serializeJson(doc2, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
 
-    REQUIRE(doc2.nestingLimit == 42);
     REQUIRE(doc2.capacity() == doc1.capacity());
   }
 
   SECTION("Copy assignment preserves the buffer when capacity is sufficient") {
     DynamicJsonDocument doc1(1234);
     deserializeJson(doc1, "{\"hello\":\"world\"}");
-    doc1.nestingLimit = 42;
 
     DynamicJsonDocument doc2(doc1.capacity());
     doc2 = doc1;
@@ -101,14 +98,12 @@ TEST_CASE("DynamicJsonDocument copies") {
     std::string json;
     serializeJson(doc2, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc2.nestingLimit == 42);
     REQUIRE(doc2.capacity() == doc1.capacity());
   }
 
   SECTION("Copy assignment realloc the buffer when capacity is insufficient") {
     DynamicJsonDocument doc1(1234);
     deserializeJson(doc1, "{\"hello\":\"world\"}");
-    doc1.nestingLimit = 42;
     DynamicJsonDocument doc2(8);
 
     REQUIRE(doc2.capacity() < doc1.memoryUsage());
@@ -118,20 +113,17 @@ TEST_CASE("DynamicJsonDocument copies") {
     std::string json;
     serializeJson(doc2, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc2.nestingLimit == 42);
   }
 
   SECTION("Construct from StaticJsonDocument") {
     StaticJsonDocument<200> sdoc;
     deserializeJson(sdoc, "{\"hello\":\"world\"}");
-    sdoc.nestingLimit = 42;
 
     DynamicJsonDocument ddoc = sdoc;
 
     std::string json;
     serializeJson(ddoc, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(ddoc.nestingLimit == 42);
     REQUIRE(ddoc.capacity() == sdoc.capacity());
   }
 
@@ -141,13 +133,11 @@ TEST_CASE("DynamicJsonDocument copies") {
 
     StaticJsonDocument<200> sdoc;
     deserializeJson(sdoc, "{\"hello\":\"world\"}");
-    sdoc.nestingLimit = 42;
 
     ddoc = sdoc;
 
     std::string json;
     serializeJson(ddoc, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(ddoc.nestingLimit == 42);
   }
 }

+ 0 - 12
test/JsonDocument/StaticJsonDocument.cpp

@@ -33,27 +33,23 @@ TEST_CASE("StaticJsonDocument") {
     StaticJsonDocument<200> doc1, doc2;
     doc1.to<JsonVariant>().set(666);
     deserializeJson(doc2, "{\"hello\":\"world\"}");
-    doc2.nestingLimit = 42;
 
     doc1 = doc2;
 
     std::string json;
     serializeJson(doc1, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc1.nestingLimit == 42);
   }
 
   SECTION("Copy constructor") {
     StaticJsonDocument<200> doc1;
     deserializeJson(doc1, "{\"hello\":\"world\"}");
-    doc1.nestingLimit = 42;
 
     StaticJsonDocument<200> doc2 = doc1;
 
     std::string json;
     serializeJson(doc2, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc2.nestingLimit == 42);
   }
 
   SECTION("Assign from StaticJsonDocument of different capacity") {
@@ -61,14 +57,12 @@ TEST_CASE("StaticJsonDocument") {
     StaticJsonDocument<300> doc2;
     doc1.to<JsonVariant>().set(666);
     deserializeJson(doc2, "{\"hello\":\"world\"}");
-    doc2.nestingLimit = 42;
 
     doc1 = doc2;
 
     std::string json;
     serializeJson(doc1, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc1.nestingLimit == 42);
   }
 
   SECTION("Assign from DynamicJsonDocument") {
@@ -76,39 +70,33 @@ TEST_CASE("StaticJsonDocument") {
     DynamicJsonDocument doc2(4096);
     doc1.to<JsonVariant>().set(666);
     deserializeJson(doc2, "{\"hello\":\"world\"}");
-    doc2.nestingLimit = 42;
 
     doc1 = doc2;
 
     std::string json;
     serializeJson(doc1, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc1.nestingLimit == 42);
   }
 
   SECTION("Construct from StaticJsonDocument of different size") {
     StaticJsonDocument<300> doc2;
     deserializeJson(doc2, "{\"hello\":\"world\"}");
-    doc2.nestingLimit = 42;
 
     StaticJsonDocument<200> doc1 = doc2;
 
     std::string json;
     serializeJson(doc1, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc1.nestingLimit == 42);
   }
 
   SECTION("Construct from DynamicJsonDocument") {
     DynamicJsonDocument doc2(4096);
     deserializeJson(doc2, "{\"hello\":\"world\"}");
-    doc2.nestingLimit = 42;
 
     StaticJsonDocument<200> doc1 = doc2;
 
     std::string json;
     serializeJson(doc1, json);
     REQUIRE(json == "{\"hello\":\"world\"}");
-    REQUIRE(doc1.nestingLimit == 42);
   }
 }

+ 70 - 17
test/MsgPackDeserializer/nestingLimit.cpp

@@ -5,28 +5,81 @@
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-static void check(const char* input, DeserializationError expected,
-                  uint8_t limit) {
+#define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
+#define SHOULD_FAIL(expression) \
+  REQUIRE(DeserializationError::TooDeep == expression);
+
+TEST_CASE("JsonDeserializer nesting") {
   DynamicJsonDocument doc(4096);
-  doc.nestingLimit = limit;
 
-  DeserializationError error = deserializeMsgPack(doc, input);
+  SECTION("Input = const char*") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeMsgPack(doc, "\xA1H", nesting));  // "H"
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x90", nesting));   // []
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x80", nesting));   // {}
+    }
 
-  REQUIRE(error == expected);
-}
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeMsgPack(doc, "\x90", nesting));           // {}
+      SHOULD_WORK(deserializeMsgPack(doc, "\x80", nesting));           // []
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x81\xA1H\x80", nesting));  // {H:{}}
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x91\x90", nesting));       // [[]]
+    }
+  }
 
-TEST_CASE("Errors returned by deserializeMsgPack()") {
-  SECTION("object too deep") {
-    check("\x80", DeserializationError::TooDeep, 0);           // {}
-    check("\x80", DeserializationError::Ok, 1);                // {}
-    check("\x81\xA1H\x80", DeserializationError::TooDeep, 1);  // {H:{}}
-    check("\x81\xA1H\x80", DeserializationError::Ok, 2);       // {H:{}}
+  SECTION("char* and size_t") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeMsgPack(doc, "\xA1H", 2, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x90", 1, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x80", 1, nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeMsgPack(doc, "\x90", 1, nesting));
+      SHOULD_WORK(deserializeMsgPack(doc, "\x80", 1, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x81\xA1H\x80", 4, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, "\x91\x90", 2, nesting));
+    }
   }
 
-  SECTION("array too deep") {
-    check("\x90", DeserializationError::TooDeep, 0);      // []
-    check("\x90", DeserializationError::Ok, 1);           // []
-    check("\x91\x90", DeserializationError::TooDeep, 1);  // [[]]
-    check("\x91\x90", DeserializationError::Ok, 2);       // [[]]
+  SECTION("Input = std::string") {
+    using std::string;
+
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      SHOULD_WORK(deserializeMsgPack(doc, string("\xA1H"), nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, string("\x90"), nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, string("\x80"), nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      SHOULD_WORK(deserializeMsgPack(doc, string("\x90"), nesting));
+      SHOULD_WORK(deserializeMsgPack(doc, string("\x80"), nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, string("\x81\xA1H\x80"), nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, string("\x91\x90"), nesting));
+    }
+  }
+
+  SECTION("Input = std::istream") {
+    SECTION("limit = 0") {
+      DeserializationOption::NestingLimit nesting(0);
+      std::istringstream good("\xA1H");  // "H"
+      std::istringstream bad("\x90");    // []
+      SHOULD_WORK(deserializeMsgPack(doc, good, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, bad, nesting));
+    }
+
+    SECTION("limit = 1") {
+      DeserializationOption::NestingLimit nesting(1);
+      std::istringstream good("\x90");     // []
+      std::istringstream bad("\x91\x90");  // [[]]
+      SHOULD_WORK(deserializeMsgPack(doc, good, nesting));
+      SHOULD_FAIL(deserializeMsgPack(doc, bad, nesting));
+    }
   }
 }