forked from bblanchon/ArduinoJson
Reduced the size of the pretty JSON serializer
This commit is contained in:
@ -20,6 +20,8 @@ HEAD
|
||||
* Added the ability to create/assign a `StaticJsonDocument`/`DynamicJsonDocument` from a `JsonArray`/`JsonObject`/`JsonVariant`
|
||||
* Added `JsonDocument::isNull()`
|
||||
* Added `JsonDocument::operator[]`
|
||||
* Added `ARDUINOJSON_TAB` to configure the indentation character
|
||||
* Reduced the size of the pretty JSON serializer
|
||||
|
||||
> ### BREAKING CHANGES
|
||||
>
|
||||
|
@ -39,13 +39,13 @@ void setup() {
|
||||
data.add(48.756080);
|
||||
data.add(2.302038);
|
||||
|
||||
serializeJson(root, Serial);
|
||||
serializeJson(doc, Serial);
|
||||
// This prints:
|
||||
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
|
||||
|
||||
Serial.println();
|
||||
|
||||
serializeJsonPretty(root, Serial);
|
||||
serializeJsonPretty(doc, Serial);
|
||||
// This prints:
|
||||
// {
|
||||
// "sensor": "gps",
|
||||
|
@ -79,7 +79,7 @@ void loop() {
|
||||
}
|
||||
|
||||
Serial.print(F("Sending: "));
|
||||
serializeJson(root, Serial);
|
||||
serializeJson(doc, Serial);
|
||||
Serial.println();
|
||||
|
||||
// Write response headers
|
||||
@ -89,7 +89,7 @@ void loop() {
|
||||
client.println();
|
||||
|
||||
// Write JSON document
|
||||
serializeJsonPretty(root, client);
|
||||
serializeJsonPretty(doc, client);
|
||||
|
||||
// Disconnect
|
||||
client.stop();
|
||||
|
@ -75,11 +75,11 @@ void loop() {
|
||||
Serial.print(remoteIp);
|
||||
Serial.print(F(" on port "));
|
||||
Serial.println(remotePort);
|
||||
serializeJson(root, Serial);
|
||||
serializeJson(doc, Serial);
|
||||
|
||||
// Send UDP packet
|
||||
udp.beginPacket(remoteIp, remotePort);
|
||||
serializeJson(root, udp);
|
||||
serializeJson(doc, udp);
|
||||
udp.println();
|
||||
udp.endPacket();
|
||||
|
||||
|
@ -140,3 +140,7 @@
|
||||
#define ARDUINOJSON_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_TAB
|
||||
#define ARDUINOJSON_TAB " "
|
||||
#endif
|
||||
|
@ -1,69 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// Decorator on top of Print to allow indented output.
|
||||
// This class is used by serializeJsonPretty() but can also be used
|
||||
// for your own purpose, like logging.
|
||||
template <typename Print>
|
||||
class IndentedPrint {
|
||||
public:
|
||||
explicit IndentedPrint(Print &p) : sink(&p) {
|
||||
level = 0;
|
||||
tabSize = 2;
|
||||
isNewLine = true;
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
size_t n = 0;
|
||||
if (isNewLine) n += writeTabs();
|
||||
n += sink->write(c);
|
||||
isNewLine = c == '\n';
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t write(const uint8_t *s, size_t n) {
|
||||
// TODO: optimize
|
||||
size_t bytesWritten = 0;
|
||||
while (n > 0) {
|
||||
bytesWritten += write(*s++);
|
||||
n--;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// Adds one level of indentation
|
||||
void indent() {
|
||||
if (level < MAX_LEVEL) level++;
|
||||
}
|
||||
|
||||
// Removes one level of indentation
|
||||
void unindent() {
|
||||
if (level > 0) level--;
|
||||
}
|
||||
|
||||
// Set the number of space printed for each level of indentation
|
||||
void setTabSize(uint8_t n) {
|
||||
if (n < MAX_TAB_SIZE) tabSize = n & MAX_TAB_SIZE;
|
||||
}
|
||||
|
||||
private:
|
||||
Print *sink;
|
||||
uint8_t level : 4;
|
||||
uint8_t tabSize : 3;
|
||||
bool isNewLine : 1;
|
||||
|
||||
size_t writeTabs() {
|
||||
size_t n = 0;
|
||||
for (int i = 0; i < level * tabSize; i++) n += sink->write(' ');
|
||||
return n;
|
||||
}
|
||||
|
||||
static const int MAX_LEVEL = 15; // because it's only 4 bits
|
||||
static const int MAX_TAB_SIZE = 7; // because it's only 3 bits
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -7,21 +7,17 @@
|
||||
#include "../Misc/Visitable.hpp"
|
||||
#include "../Serialization/measure.hpp"
|
||||
#include "../Serialization/serialize.hpp"
|
||||
#include "JsonWriter.hpp"
|
||||
#include "TextFormatter.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class JsonSerializer {
|
||||
public:
|
||||
JsonSerializer(TWriter &writer) : _writer(writer) {}
|
||||
JsonSerializer(TWriter &writer) : _formatter(writer) {}
|
||||
|
||||
void visitFloat(Float value) {
|
||||
_writer.writeFloat(value);
|
||||
}
|
||||
|
||||
void visitArray(const CollectionData &array) {
|
||||
_writer.beginArray();
|
||||
FORCE_INLINE void visitArray(const CollectionData &array) {
|
||||
write('[');
|
||||
|
||||
VariantSlot *slot = array.head();
|
||||
|
||||
@ -31,63 +27,74 @@ class JsonSerializer {
|
||||
slot = slot->next();
|
||||
if (slot == 0) break;
|
||||
|
||||
_writer.writeComma();
|
||||
write(',');
|
||||
}
|
||||
|
||||
_writer.endArray();
|
||||
write(']');
|
||||
}
|
||||
|
||||
void visitObject(const CollectionData &object) {
|
||||
_writer.beginObject();
|
||||
write('{');
|
||||
|
||||
VariantSlot *slot = object.head();
|
||||
|
||||
while (slot != 0) {
|
||||
_writer.writeString(slot->key());
|
||||
_writer.writeColon();
|
||||
_formatter.writeString(slot->key());
|
||||
write(':');
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
if (slot == 0) break;
|
||||
|
||||
_writer.writeComma();
|
||||
write(',');
|
||||
}
|
||||
|
||||
_writer.endObject();
|
||||
write('}');
|
||||
}
|
||||
|
||||
void visitFloat(Float value) {
|
||||
_formatter.writeFloat(value);
|
||||
}
|
||||
|
||||
void visitString(const char *value) {
|
||||
_writer.writeString(value);
|
||||
_formatter.writeString(value);
|
||||
}
|
||||
|
||||
void visitRawJson(const char *data, size_t n) {
|
||||
// TODO
|
||||
for (size_t i = 0; i < n; i++) _writer.writeRaw(data[i]);
|
||||
_formatter.writeRaw(data, n);
|
||||
}
|
||||
|
||||
void visitNegativeInteger(UInt value) {
|
||||
_writer.writeRaw('-');
|
||||
_writer.writeInteger(value);
|
||||
_formatter.writeNegativeInteger(value);
|
||||
}
|
||||
|
||||
void visitPositiveInteger(UInt value) {
|
||||
_writer.writeInteger(value);
|
||||
_formatter.writePositiveInteger(value);
|
||||
}
|
||||
|
||||
void visitBoolean(bool value) {
|
||||
_writer.writeBoolean(value);
|
||||
_formatter.writeBoolean(value);
|
||||
}
|
||||
|
||||
void visitNull() {
|
||||
_writer.writeRaw("null");
|
||||
_formatter.writeRaw("null");
|
||||
}
|
||||
|
||||
size_t bytesWritten() const {
|
||||
return _writer.bytesWritten();
|
||||
return _formatter.bytesWritten();
|
||||
}
|
||||
|
||||
protected:
|
||||
void write(char c) {
|
||||
_formatter.writeRaw(c);
|
||||
}
|
||||
|
||||
void write(const char *s) {
|
||||
_formatter.writeRaw(s);
|
||||
}
|
||||
|
||||
private:
|
||||
JsonWriter<TWriter> _writer;
|
||||
TextFormatter<TWriter> _formatter;
|
||||
};
|
||||
|
||||
template <typename TSource, typename TDestination>
|
||||
|
@ -4,37 +4,68 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Configuration.hpp"
|
||||
#include "../Serialization/measure.hpp"
|
||||
#include "../Serialization/serialize.hpp"
|
||||
#include "./IndentedPrint.hpp"
|
||||
#include "./JsonSerializer.hpp"
|
||||
#include "./Prettyfier.hpp"
|
||||
#include "JsonSerializer.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TPrint>
|
||||
class PrettyJsonSerializer_Base {
|
||||
public:
|
||||
PrettyJsonSerializer_Base(TPrint &output)
|
||||
: _indentedPrint(output), _prettyfier(_indentedPrint) {}
|
||||
template <typename TWriter>
|
||||
class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
typedef JsonSerializer<TWriter> base;
|
||||
|
||||
protected:
|
||||
IndentedPrint<TPrint> _indentedPrint;
|
||||
Prettyfier<TPrint> _prettyfier;
|
||||
};
|
||||
|
||||
template <typename TPrint>
|
||||
class PrettyJsonSerializer : PrettyJsonSerializer_Base<TPrint>,
|
||||
public JsonSerializer<Prettyfier<TPrint> > {
|
||||
public:
|
||||
PrettyJsonSerializer(TPrint &output)
|
||||
: PrettyJsonSerializer_Base<TPrint>(output),
|
||||
JsonSerializer<Prettyfier<TPrint> >(
|
||||
PrettyJsonSerializer_Base<TPrint>::_prettyfier) {}
|
||||
PrettyJsonSerializer(TWriter &writer) : base(writer), _nesting(0) {}
|
||||
|
||||
void visitArray(const CollectionData &array) {
|
||||
VariantSlot *slot = array.head();
|
||||
if (!slot) return base::write("[]");
|
||||
|
||||
base::write("[\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("]");
|
||||
}
|
||||
|
||||
void visitObject(const CollectionData &object) {
|
||||
VariantSlot *slot = object.head();
|
||||
if (!slot) return base::write("{}");
|
||||
|
||||
base::write("{\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
base::visitString(slot->key());
|
||||
base::write(": ");
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("}");
|
||||
}
|
||||
|
||||
private:
|
||||
void indent() {
|
||||
for (uint8_t i = 0; i < _nesting; i++) base::write(ARDUINOJSON_TAB);
|
||||
}
|
||||
|
||||
uint8_t _nesting;
|
||||
};
|
||||
|
||||
template <typename TSource, typename TDestination>
|
||||
size_t serializeJsonPretty(TSource &source, TDestination &destination) {
|
||||
size_t serializeJsonPretty(const TSource &source, TDestination &destination) {
|
||||
return serialize<PrettyJsonSerializer>(source, destination);
|
||||
}
|
||||
|
||||
|
@ -1,143 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IndentedPrint.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// Converts a compact JSON string into an indented one.
|
||||
template <typename TWriter>
|
||||
class Prettyfier {
|
||||
public:
|
||||
explicit Prettyfier(IndentedPrint<TWriter>& p) : _sink(p) {
|
||||
_previousChar = 0;
|
||||
_inString = false;
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
size_t n = _inString ? handleStringChar(c) : handleMarkupChar(char(c));
|
||||
_previousChar = char(c);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* s, size_t n) {
|
||||
// TODO: optimize
|
||||
size_t bytesWritten = 0;
|
||||
while (n > 0) {
|
||||
bytesWritten += write(*s++);
|
||||
n--;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
private:
|
||||
Prettyfier& operator=(const Prettyfier&); // cannot be assigned
|
||||
|
||||
bool inEmptyBlock() {
|
||||
return _previousChar == '{' || _previousChar == '[';
|
||||
}
|
||||
|
||||
size_t handleStringChar(uint8_t c) {
|
||||
bool isQuote = c == '"' && _previousChar != '\\';
|
||||
|
||||
if (isQuote) _inString = false;
|
||||
|
||||
return _sink.write(c);
|
||||
}
|
||||
|
||||
size_t handleMarkupChar(char c) {
|
||||
switch (c) {
|
||||
case '{':
|
||||
case '[':
|
||||
return writeBlockOpen(c);
|
||||
|
||||
case '}':
|
||||
case ']':
|
||||
return writeBlockClose(c);
|
||||
|
||||
case ':':
|
||||
return writeColon();
|
||||
|
||||
case ',':
|
||||
return writeComma();
|
||||
|
||||
case '"':
|
||||
return writeQuoteOpen();
|
||||
|
||||
default:
|
||||
return writeNormalChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
size_t writeBlockClose(char c) {
|
||||
size_t n = 0;
|
||||
n += unindentIfNeeded();
|
||||
n += write(c);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t writeBlockOpen(char c) {
|
||||
size_t n = 0;
|
||||
n += indentIfNeeded();
|
||||
n += write(c);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t writeColon() {
|
||||
size_t n = 0;
|
||||
n += write(": ");
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t writeComma() {
|
||||
size_t n = 0;
|
||||
n += write(",\r\n");
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t writeQuoteOpen() {
|
||||
_inString = true;
|
||||
size_t n = 0;
|
||||
n += indentIfNeeded();
|
||||
n += write('"');
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t writeNormalChar(char c) {
|
||||
size_t n = 0;
|
||||
n += indentIfNeeded();
|
||||
n += write(c);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t indentIfNeeded() {
|
||||
if (!inEmptyBlock()) return 0;
|
||||
|
||||
_sink.indent();
|
||||
return write("\r\n");
|
||||
}
|
||||
|
||||
size_t unindentIfNeeded() {
|
||||
if (inEmptyBlock()) return 0;
|
||||
|
||||
_sink.unindent();
|
||||
return write("\r\n");
|
||||
}
|
||||
|
||||
size_t write(char c) {
|
||||
return _sink.write(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
size_t write(const char (&s)[N]) {
|
||||
return _sink.write(reinterpret_cast<const uint8_t*>(s), N - 1);
|
||||
}
|
||||
|
||||
char _previousChar;
|
||||
IndentedPrint<TWriter>& _sink;
|
||||
bool _inString;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -14,36 +14,15 @@
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class JsonWriter {
|
||||
class TextFormatter {
|
||||
public:
|
||||
explicit JsonWriter(TWriter &writer) : _writer(writer), _length(0) {}
|
||||
explicit TextFormatter(TWriter &writer) : _writer(writer), _length(0) {}
|
||||
|
||||
// Returns the number of bytes sent to the TWriter implementation.
|
||||
size_t bytesWritten() const {
|
||||
return _length;
|
||||
}
|
||||
|
||||
void beginArray() {
|
||||
writeRaw('[');
|
||||
}
|
||||
void endArray() {
|
||||
writeRaw(']');
|
||||
}
|
||||
|
||||
void beginObject() {
|
||||
writeRaw('{');
|
||||
}
|
||||
void endObject() {
|
||||
writeRaw('}');
|
||||
}
|
||||
|
||||
void writeColon() {
|
||||
writeRaw(':');
|
||||
}
|
||||
void writeComma() {
|
||||
writeRaw(',');
|
||||
}
|
||||
|
||||
void writeBoolean(bool value) {
|
||||
if (value)
|
||||
writeRaw("true");
|
||||
@ -84,22 +63,27 @@ class JsonWriter {
|
||||
|
||||
FloatParts<T> parts(value);
|
||||
|
||||
writeInteger(parts.integral);
|
||||
writePositiveInteger(parts.integral);
|
||||
if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces);
|
||||
|
||||
if (parts.exponent < 0) {
|
||||
writeRaw("e-");
|
||||
writeInteger(-parts.exponent);
|
||||
writePositiveInteger(-parts.exponent);
|
||||
}
|
||||
|
||||
if (parts.exponent > 0) {
|
||||
writeRaw('e');
|
||||
writeInteger(parts.exponent);
|
||||
writePositiveInteger(parts.exponent);
|
||||
}
|
||||
}
|
||||
|
||||
void writeNegativeInteger(UInt value) {
|
||||
writeRaw('-');
|
||||
writePositiveInteger(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeInteger(T value) {
|
||||
void writePositiveInteger(T value) {
|
||||
char buffer[22];
|
||||
char *end = buffer + sizeof(buffer);
|
||||
char *begin = end;
|
||||
@ -134,10 +118,16 @@ class JsonWriter {
|
||||
void writeRaw(const char *s) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
|
||||
}
|
||||
|
||||
void writeRaw(const char *s, size_t n) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), n);
|
||||
}
|
||||
|
||||
void writeRaw(const char *begin, const char *end) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(begin),
|
||||
static_cast<size_t>(end - begin));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void writeRaw(const char (&s)[N]) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
|
||||
@ -151,6 +141,6 @@ class JsonWriter {
|
||||
size_t _length;
|
||||
|
||||
private:
|
||||
JsonWriter &operator=(const JsonWriter &); // cannot be assigned
|
||||
TextFormatter &operator=(const TextFormatter &); // cannot be assigned
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -78,7 +78,7 @@ add_subdirectory(JsonDocument)
|
||||
add_subdirectory(JsonObject)
|
||||
add_subdirectory(JsonSerializer)
|
||||
add_subdirectory(JsonVariant)
|
||||
add_subdirectory(JsonWriter)
|
||||
add_subdirectory(TextFormatter)
|
||||
add_subdirectory(MemoryPool)
|
||||
add_subdirectory(Misc)
|
||||
add_subdirectory(MixedConfiguration)
|
||||
|
@ -8,4 +8,4 @@ add_executable(JsonWriterTests
|
||||
)
|
||||
|
||||
target_link_libraries(JsonWriterTests catch)
|
||||
add_test(JsonWriter JsonWriterTests)
|
||||
add_test(TextFormatter JsonWriterTests)
|
@ -6,7 +6,7 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include <ArduinoJson/Json/JsonWriter.hpp>
|
||||
#include <ArduinoJson/Json/TextFormatter.hpp>
|
||||
#include <ArduinoJson/Serialization/DynamicStringWriter.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
@ -15,13 +15,13 @@ template <typename TFloat>
|
||||
void check(TFloat input, const std::string& expected) {
|
||||
std::string output;
|
||||
DynamicStringWriter<std::string> sb(output);
|
||||
JsonWriter<DynamicStringWriter<std::string> > writer(sb);
|
||||
TextFormatter<DynamicStringWriter<std::string> > writer(sb);
|
||||
writer.writeFloat(input);
|
||||
REQUIRE(writer.bytesWritten() == output.size());
|
||||
CHECK(expected == output);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonWriter::writeFloat(double)") {
|
||||
TEST_CASE("TextFormatter::writeFloat(double)") {
|
||||
SECTION("Pi") {
|
||||
check<double>(3.14159265359, "3.141592654");
|
||||
}
|
||||
@ -102,7 +102,7 @@ TEST_CASE("JsonWriter::writeFloat(double)") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonWriter::writeFloat(float)") {
|
||||
TEST_CASE("TextFormatter::writeFloat(float)") {
|
||||
SECTION("Pi") {
|
||||
check<float>(3.14159265359f, "3.141593");
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <ArduinoJson/Json/JsonWriter.hpp>
|
||||
#include <ArduinoJson/Json/TextFormatter.hpp>
|
||||
#include <ArduinoJson/Serialization/StaticStringWriter.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
@ -12,13 +12,13 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
void check(const char* input, std::string expected) {
|
||||
char output[1024];
|
||||
StaticStringWriter sb(output, sizeof(output));
|
||||
JsonWriter<StaticStringWriter> writer(sb);
|
||||
TextFormatter<StaticStringWriter> writer(sb);
|
||||
writer.writeString(input);
|
||||
REQUIRE(expected == output);
|
||||
REQUIRE(writer.bytesWritten() == expected.size());
|
||||
}
|
||||
|
||||
TEST_CASE("JsonWriter::writeString()") {
|
||||
TEST_CASE("TextFormatter::writeString()") {
|
||||
SECTION("Null") {
|
||||
check(0, "null");
|
||||
}
|
Reference in New Issue
Block a user