mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-18 21:12:25 +02:00
365 lines
9.4 KiB
C++
365 lines
9.4 KiB
C++
// ArduinoJson - arduinojson.org
|
|
// Copyright Benoit Blanchon 2014-2018
|
|
// MIT License
|
|
|
|
#pragma once
|
|
|
|
#include "../Deserialization/deserialize.hpp"
|
|
#include "../Memory/MemoryPool.hpp"
|
|
#include "../Numbers/isFloat.hpp"
|
|
#include "../Numbers/isInteger.hpp"
|
|
#include "../Polyfills/type_traits.hpp"
|
|
#include "../Variant/VariantRef.hpp"
|
|
#include "EscapeSequence.hpp"
|
|
|
|
namespace ARDUINOJSON_NAMESPACE {
|
|
|
|
template <typename TReader, typename TStringStorage>
|
|
class JsonDeserializer {
|
|
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
|
StringBuilder;
|
|
typedef typename StringBuilder::StringType StringType;
|
|
|
|
public:
|
|
JsonDeserializer(MemoryPool &memoryPool, TReader reader,
|
|
TStringStorage stringStorage, uint8_t nestingLimit)
|
|
: _memoryPool(&memoryPool),
|
|
_reader(reader),
|
|
_stringStorage(stringStorage),
|
|
_nestingLimit(nestingLimit),
|
|
_loaded(false) {}
|
|
DeserializationError parse(VariantRef variant) {
|
|
DeserializationError err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
|
|
switch (current()) {
|
|
case '[':
|
|
return parseArray(variant);
|
|
|
|
case '{':
|
|
return parseObject(variant);
|
|
|
|
default:
|
|
return parseValue(variant);
|
|
}
|
|
}
|
|
|
|
private:
|
|
JsonDeserializer &operator=(const JsonDeserializer &); // non-copiable
|
|
|
|
char current() {
|
|
if (!_loaded) {
|
|
if (_reader.ended())
|
|
_current = 0;
|
|
else
|
|
_current = _reader.read();
|
|
_loaded = true;
|
|
}
|
|
return _current;
|
|
}
|
|
|
|
void move() {
|
|
_loaded = false;
|
|
}
|
|
|
|
FORCE_INLINE bool eat(char charToSkip) {
|
|
if (current() != charToSkip) return false;
|
|
move();
|
|
return true;
|
|
}
|
|
|
|
DeserializationError parseArray(VariantRef variant) {
|
|
if (_nestingLimit == 0) return DeserializationError::TooDeep;
|
|
|
|
ArrayRef array = variant.to<ArrayRef>();
|
|
if (array.isNull()) return DeserializationError::NoMemory;
|
|
|
|
// Check opening braket
|
|
if (!eat('[')) return DeserializationError::InvalidInput;
|
|
|
|
// Skip spaces
|
|
DeserializationError err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
|
|
// Empty array?
|
|
if (eat(']')) return DeserializationError::Ok;
|
|
|
|
// Read each value
|
|
for (;;) {
|
|
// Allocate slot in array
|
|
VariantRef value = array.add();
|
|
if (value.isInvalid()) return DeserializationError::NoMemory;
|
|
|
|
// 1 - Parse value
|
|
_nestingLimit--;
|
|
err = parse(value);
|
|
_nestingLimit++;
|
|
if (err) return err;
|
|
|
|
// 2 - Skip spaces
|
|
err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
|
|
// 3 - More values?
|
|
if (eat(']')) return DeserializationError::Ok;
|
|
if (!eat(',')) return DeserializationError::InvalidInput;
|
|
}
|
|
}
|
|
|
|
DeserializationError parseObject(VariantRef variant) {
|
|
if (_nestingLimit == 0) return DeserializationError::TooDeep;
|
|
|
|
ObjectRef object = variant.to<ObjectRef>();
|
|
if (object.isNull()) return DeserializationError::NoMemory;
|
|
|
|
// Check opening brace
|
|
if (!eat('{')) return DeserializationError::InvalidInput;
|
|
|
|
// Skip spaces
|
|
DeserializationError err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
|
|
// Empty object?
|
|
if (eat('}')) return DeserializationError::Ok;
|
|
|
|
// Read each key value pair
|
|
for (;;) {
|
|
// Parse key
|
|
StringType key;
|
|
err = parseKey(key);
|
|
if (err) return err;
|
|
|
|
// Skip spaces
|
|
err = skipSpacesAndComments();
|
|
if (err) return err; // Colon
|
|
if (!eat(':')) return DeserializationError::InvalidInput;
|
|
|
|
// Allocate slot in object
|
|
VariantRef value = object.set(key);
|
|
if (value.isInvalid()) return DeserializationError::NoMemory;
|
|
|
|
// Parse value
|
|
_nestingLimit--;
|
|
err = parse(value);
|
|
_nestingLimit++;
|
|
if (err) return err;
|
|
|
|
// Skip spaces
|
|
err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
|
|
// More keys/values?
|
|
if (eat('}')) return DeserializationError::Ok;
|
|
if (!eat(',')) return DeserializationError::InvalidInput;
|
|
|
|
// Skip spaces
|
|
err = skipSpacesAndComments();
|
|
if (err) return err;
|
|
}
|
|
}
|
|
|
|
DeserializationError parseValue(VariantRef variant) {
|
|
if (isQuote(current())) {
|
|
return parseStringValue(variant);
|
|
} else {
|
|
return parseNumericValue(variant);
|
|
}
|
|
}
|
|
|
|
DeserializationError parseKey(StringType &key) {
|
|
if (isQuote(current())) {
|
|
return parseQuotedString(key);
|
|
} else {
|
|
return parseNonQuotedString(key);
|
|
}
|
|
}
|
|
|
|
DeserializationError parseStringValue(VariantRef variant) {
|
|
StringType value;
|
|
DeserializationError err = parseQuotedString(value);
|
|
if (err) return err;
|
|
variant.set(value);
|
|
return DeserializationError::Ok;
|
|
}
|
|
|
|
DeserializationError parseQuotedString(StringType &result) {
|
|
StringBuilder builder = _stringStorage.startString();
|
|
const char stopChar = current();
|
|
|
|
move();
|
|
for (;;) {
|
|
char c = current();
|
|
move();
|
|
if (c == stopChar) break;
|
|
|
|
if (c == '\0') return DeserializationError::IncompleteInput;
|
|
|
|
if (c == '\\') {
|
|
c = current();
|
|
if (c == '\0') return DeserializationError::IncompleteInput;
|
|
if (c == 'u') return DeserializationError::NotSupported;
|
|
// replace char
|
|
c = EscapeSequence::unescapeChar(c);
|
|
if (c == '\0') return DeserializationError::InvalidInput;
|
|
move();
|
|
}
|
|
|
|
builder.append(c);
|
|
}
|
|
|
|
result = builder.complete();
|
|
if (result.isNull()) return DeserializationError::NoMemory;
|
|
return DeserializationError::Ok;
|
|
}
|
|
|
|
DeserializationError parseNonQuotedString(StringType &result) {
|
|
StringBuilder builder = _stringStorage.startString();
|
|
|
|
char c = current();
|
|
if (c == '\0') return DeserializationError::IncompleteInput;
|
|
|
|
if (canBeInNonQuotedString(c)) { // no quotes
|
|
do {
|
|
move();
|
|
builder.append(c);
|
|
c = current();
|
|
} while (canBeInNonQuotedString(c));
|
|
} else {
|
|
return DeserializationError::InvalidInput;
|
|
}
|
|
|
|
result = builder.complete();
|
|
if (result.isNull()) return DeserializationError::NoMemory;
|
|
return DeserializationError::Ok;
|
|
}
|
|
|
|
DeserializationError parseNumericValue(VariantRef result) {
|
|
char buffer[64];
|
|
uint8_t n = 0;
|
|
|
|
char c = current();
|
|
while (canBeInNonQuotedString(c) && n < 63) {
|
|
move();
|
|
buffer[n++] = c;
|
|
c = current();
|
|
}
|
|
buffer[n] = 0;
|
|
|
|
if (isInteger(buffer)) {
|
|
result.set(parseInteger<Integer>(buffer));
|
|
} else if (isFloat(buffer)) {
|
|
result.set(parseFloat<Float>(buffer));
|
|
} else if (!strcmp(buffer, "true")) {
|
|
result.set(true);
|
|
} else if (!strcmp(buffer, "false")) {
|
|
result.set(false);
|
|
} else if (!strcmp(buffer, "null")) {
|
|
// already null
|
|
} else {
|
|
return DeserializationError::InvalidInput;
|
|
}
|
|
return DeserializationError::Ok;
|
|
}
|
|
|
|
static inline bool isBetween(char c, char min, char max) {
|
|
return min <= c && c <= max;
|
|
}
|
|
|
|
static inline bool canBeInNonQuotedString(char c) {
|
|
return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
|
|
isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
|
|
}
|
|
|
|
static inline bool isQuote(char c) {
|
|
return c == '\'' || c == '\"';
|
|
}
|
|
|
|
DeserializationError skipSpacesAndComments() {
|
|
for (;;) {
|
|
switch (current()) {
|
|
// end of string
|
|
case '\0':
|
|
return DeserializationError::IncompleteInput;
|
|
|
|
// spaces
|
|
case ' ':
|
|
case '\t':
|
|
case '\r':
|
|
case '\n':
|
|
move();
|
|
continue;
|
|
|
|
// comments
|
|
case '/':
|
|
move(); // skip '/'
|
|
switch (current()) {
|
|
// block comment
|
|
case '*': {
|
|
move(); // skip '*'
|
|
bool wasStar = false;
|
|
for (;;) {
|
|
char c = current();
|
|
if (c == '\0') return DeserializationError::IncompleteInput;
|
|
if (c == '/' && wasStar) {
|
|
move();
|
|
break;
|
|
}
|
|
wasStar = c == '*';
|
|
move();
|
|
}
|
|
break;
|
|
}
|
|
|
|
// trailing comment
|
|
case '/':
|
|
// no need to skip "//"
|
|
for (;;) {
|
|
move();
|
|
char c = current();
|
|
if (c == '\0') return DeserializationError::IncompleteInput;
|
|
if (c == '\n') break;
|
|
}
|
|
break;
|
|
|
|
// not a comment, just a '/'
|
|
default:
|
|
return DeserializationError::InvalidInput;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return DeserializationError::Ok;
|
|
}
|
|
}
|
|
}
|
|
|
|
MemoryPool *_memoryPool;
|
|
TReader _reader;
|
|
TStringStorage _stringStorage;
|
|
uint8_t _nestingLimit;
|
|
char _current;
|
|
bool _loaded;
|
|
};
|
|
|
|
template <typename TDocument, typename TInput>
|
|
DeserializationError deserializeJson(TDocument &doc, const TInput &input) {
|
|
return deserialize<JsonDeserializer>(doc, input);
|
|
}
|
|
|
|
template <typename TDocument, typename TInput>
|
|
DeserializationError deserializeJson(TDocument &doc, TInput *input) {
|
|
return deserialize<JsonDeserializer>(doc, input);
|
|
}
|
|
|
|
template <typename TDocument, typename TInput>
|
|
DeserializationError deserializeJson(TDocument &doc, TInput *input,
|
|
size_t inputSize) {
|
|
return deserialize<JsonDeserializer>(doc, input, inputSize);
|
|
}
|
|
|
|
template <typename TDocument, typename TInput>
|
|
DeserializationError deserializeJson(TDocument &doc, TInput &input) {
|
|
return deserialize<JsonDeserializer>(doc, input);
|
|
}
|
|
} // namespace ARDUINOJSON_NAMESPACE
|