Selaa lähdekoodia

Reduced the size of the pretty JSON serializer

Benoit Blanchon 7 vuotta sitten
vanhempi
sitoutus
70739f5cfd

+ 2 - 0
CHANGELOG.md

@@ -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
 > 

+ 2 - 2
examples/JsonGeneratorExample/JsonGeneratorExample.ino

@@ -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",

+ 2 - 2
examples/JsonServer/JsonServer.ino

@@ -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();

+ 2 - 2
examples/JsonUdpBeacon/JsonUdpBeacon.ino

@@ -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();
 

+ 4 - 0
src/ArduinoJson/Configuration.hpp

@@ -140,3 +140,7 @@
 #define ARDUINOJSON_LITTLE_ENDIAN 0
 #endif
 #endif
+
+#ifndef ARDUINOJSON_TAB
+#define ARDUINOJSON_TAB "  "
+#endif

+ 0 - 69
src/ArduinoJson/Json/IndentedPrint.hpp

@@ -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

+ 32 - 25
src/ArduinoJson/Json/JsonSerializer.hpp

@@ -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>

+ 51 - 20
src/ArduinoJson/Json/PrettyJsonSerializer.hpp

@@ -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 {
+template <typename TWriter>
+class PrettyJsonSerializer : public JsonSerializer<TWriter> {
+  typedef JsonSerializer<TWriter> base;
+
  public:
-  PrettyJsonSerializer_Base(TPrint &output)
-      : _indentedPrint(output), _prettyfier(_indentedPrint) {}
+  PrettyJsonSerializer(TWriter &writer) : base(writer), _nesting(0) {}
 
- protected:
-  IndentedPrint<TPrint> _indentedPrint;
-  Prettyfier<TPrint> _prettyfier;
-};
+  void visitArray(const CollectionData &array) {
+    VariantSlot *slot = array.head();
+    if (!slot) return base::write("[]");
 
-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) {}
+    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);
 }
 

+ 0 - 143
src/ArduinoJson/Json/Prettyfier.hpp

@@ -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

+ 18 - 28
src/ArduinoJson/Json/JsonWriter.hpp → src/ArduinoJson/Json/TextFormatter.hpp

@@ -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

+ 1 - 1
test/CMakeLists.txt

@@ -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)

+ 1 - 1
test/JsonWriter/CMakeLists.txt → test/TextFormatter/CMakeLists.txt

@@ -8,4 +8,4 @@ add_executable(JsonWriterTests
 )
 
 target_link_libraries(JsonWriterTests catch)
-add_test(JsonWriter JsonWriterTests)
+add_test(TextFormatter JsonWriterTests)

+ 4 - 4
test/JsonWriter/writeFloat.cpp → test/TextFormatter/writeFloat.cpp

@@ -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");
   }

+ 3 - 3
test/JsonWriter/writeString.cpp → test/TextFormatter/writeString.cpp

@@ -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");
   }