Skip to content

Commit

Permalink
Added support for custom writer classes (issue #1088)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Sep 13, 2019
1 parent 2078871 commit d09ae95
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 154 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,11 @@
ArduinoJson: change log
=======================

HEAD
----

* Added support for custom writer class (issue #1088)

v6.12.0 (2019-09-05)
-------

Expand Down
1 change: 1 addition & 0 deletions extras/tests/JsonSerializer/CMakeLists.txt
Expand Up @@ -3,6 +3,7 @@
# MIT License

add_executable(JsonSerializerTests
CustomWriter.cpp
JsonArray.cpp
JsonArrayPretty.cpp
JsonObject.cpp
Expand Down
41 changes: 41 additions & 0 deletions extras/tests/JsonSerializer/CustomWriter.cpp
@@ -0,0 +1,41 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License

#include <ArduinoJson.h>
#include <catch.hpp>

struct CustomWriter {
std::string str;

size_t write(uint8_t c) {
str.append(1, static_cast<char>(c));
return 1;
}

size_t write(const uint8_t *s, size_t n) {
str.append(reinterpret_cast<const char *>(s), n);
return n;
}
};

TEST_CASE("CustomWriter") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
array.add(4);
array.add(2);

SECTION("serializeJson()") {
CustomWriter writer;
serializeJson(array, writer);

REQUIRE("[4,2]" == writer.str);
}

SECTION("serializeJsonPretty") {
CustomWriter writer;
serializeJsonPretty(array, writer);

REQUIRE("[\r\n 4,\r\n 2\r\n]" == writer.str);
}
}
6 changes: 3 additions & 3 deletions extras/tests/TextFormatter/CMakeLists.txt
Expand Up @@ -2,10 +2,10 @@
# Copyright Benoit Blanchon 2014-2019
# MIT License

add_executable(JsonWriterTests
add_executable(TextFormatterTests
writeFloat.cpp
writeString.cpp
)

target_link_libraries(JsonWriterTests catch)
add_test(TextFormatter JsonWriterTests)
target_link_libraries(TextFormatterTests catch)
add_test(TextFormatter TextFormatterTests)
250 changes: 126 additions & 124 deletions src/ArduinoJson/Json/TextFormatter.hpp
Expand Up @@ -12,145 +12,147 @@
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Polyfills/attributes.hpp>

namespace ARDUINOJSON_NAMESPACE {

template <typename TWriter>
class TextFormatter {
public:
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 writeBoolean(bool value) {
if (value)
writeRaw("true");
else
writeRaw("false");
}

void writeString(const char *value) {
if (!value) {
writeRaw("null");
} else {
writeRaw('\"');
while (*value) writeChar(*value++);
writeRaw('\"');
namespace ARDUINOJSON_NAMESPACE {

template <typename TWriter>
class TextFormatter {
public:
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 writeChar(char c) {
char specialChar = EscapeSequence::escapeChar(c);
if (specialChar) {
writeRaw('\\');
writeRaw(specialChar);
} else {
writeRaw(c);

void writeBoolean(bool value) {
if (value)
writeRaw("true");
else
writeRaw("false");
}
}

template <typename T>
void writeFloat(T value) {
if (isnan(value)) return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
void writeString(const char *value) {
if (!value) {
writeRaw("null");
} else {
writeRaw('\"');
while (*value) writeChar(*value++);
writeRaw('\"');
}
}

#if ARDUINOJSON_ENABLE_INFINITY
if (value < 0.0) {
writeRaw('-');
value = -value;
void writeChar(char c) {
char specialChar = EscapeSequence::escapeChar(c);
if (specialChar) {
writeRaw('\\');
writeRaw(specialChar);
} else {
writeRaw(c);
}
}

if (isinf(value)) return writeRaw("Infinity");
template <typename T>
void writeFloat(T value) {
if (isnan(value))
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");

#if ARDUINOJSON_ENABLE_INFINITY
if (value < 0.0) {
writeRaw('-');
value = -value;
}

if (isinf(value)) return writeRaw("Infinity");
#else
if (isinf(value)) return writeRaw("null");
if (isinf(value)) return writeRaw("null");

if (value < 0.0) {
writeRaw('-');
value = -value;
}
#endif

FloatParts<T> parts(value);

writePositiveInteger(parts.integral);
if (parts.decimalPlaces)
writeDecimals(parts.decimal, parts.decimalPlaces);

if (parts.exponent < 0) {
writeRaw("e-");
writePositiveInteger(-parts.exponent);
}

if (value < 0.0) {
if (parts.exponent > 0) {
writeRaw('e');
writePositiveInteger(parts.exponent);
}
}

void writeNegativeInteger(UInt value) {
writeRaw('-');
value = -value;
writePositiveInteger(value);
}
#endif

FloatParts<T> parts(value);
template <typename T>
void writePositiveInteger(T value) {
char buffer[22];
char *end = buffer + sizeof(buffer);
char *begin = end;

writePositiveInteger(parts.integral);
if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces);
// write the string in reverse order
do {
*--begin = char(value % 10 + '0');
value = T(value / 10);
} while (value);

if (parts.exponent < 0) {
writeRaw("e-");
writePositiveInteger(-parts.exponent);
// and dump it in the right order
writeRaw(begin, end);
}

if (parts.exponent > 0) {
writeRaw('e');
writePositiveInteger(parts.exponent);
void writeDecimals(uint32_t value, int8_t width) {
// buffer should be big enough for all digits and the dot
char buffer[16];
char *end = buffer + sizeof(buffer);
char *begin = end;

// write the string in reverse order
while (width--) {
*--begin = char(value % 10 + '0');
value /= 10;
}
*--begin = '.';

// and dump it in the right order
writeRaw(begin, end);
}
}

void writeNegativeInteger(UInt value) {
writeRaw('-');
writePositiveInteger(value);
}

template <typename T>
void writePositiveInteger(T value) {
char buffer[22];
char *end = buffer + sizeof(buffer);
char *begin = end;

// write the string in reverse order
do {
*--begin = char(value % 10 + '0');
value = T(value / 10);
} while (value);

// and dump it in the right order
writeRaw(begin, end);
}

void writeDecimals(uint32_t value, int8_t width) {
// buffer should be big enough for all digits and the dot
char buffer[16];
char *end = buffer + sizeof(buffer);
char *begin = end;

// write the string in reverse order
while (width--) {
*--begin = char(value % 10 + '0');
value /= 10;

void writeRaw(const char *s) {
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
}
*--begin = '.';

// and dump it in the right order
writeRaw(begin, end);
}

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);
}
void writeRaw(char c) {
_length += _writer.write(static_cast<uint8_t>(c));
}

protected:
TWriter &_writer;
size_t _length;

private:
TextFormatter &operator=(const TextFormatter &); // cannot be assigned
};

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);
}
void writeRaw(char c) {
_length += _writer.write(static_cast<uint8_t>(c));
}

protected:
TWriter &_writer;
size_t _length;

private:
TextFormatter &operator=(const TextFormatter &); // cannot be assigned
};
} // namespace ARDUINOJSON_NAMESPACE
9 changes: 9 additions & 0 deletions src/ArduinoJson/Serialization/DynamicStringWriter.hpp
Expand Up @@ -4,7 +4,9 @@

#pragma once

#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Serialization/WriterSelector.hpp>

#if ARDUINOJSON_ENABLE_ARDUINO_STRING
#include <WString.h>
Expand Down Expand Up @@ -79,4 +81,11 @@ class DynamicStringWriter<std::basic_string<char, TCharTraits, TAllocator> > {
string_type *_str;
};
#endif

template <typename TDestination>
struct WriterSelector<
TDestination,
typename enable_if<IsWriteableString<TDestination>::value>::type> {
typedef DynamicStringWriter<TDestination> writer_type;
};
} // namespace ARDUINOJSON_NAMESPACE

0 comments on commit d09ae95

Please sign in to comment.