Added support for Stream (issue #300)

This commit is contained in:
Benoit Blanchon
2017-01-03 22:03:50 +01:00
parent 3f96e070ce
commit 55669e306e
22 changed files with 407 additions and 191 deletions

View File

@ -5,6 +5,7 @@ HEAD
----
* Added operator `==` to compare `JsonVariant` and strings (issue #402)
* Added support for `Stream` (issue #300)
* Reduced memory consumption by not duplicating spaces and comments
v5.7.3

View File

@ -5,9 +5,10 @@ Arduino JSON library
*An elegant and efficient JSON library for embedded systems.*
It's designed to have the most intuitive API, the smallest footprint and works without any allocation on the heap (no malloc).
It's designed to have the most intuitive API, the smallest footprint and is able to work without any allocation on the heap (no malloc).
It has been written with Arduino in mind, but it isn't linked to Arduino libraries so you can use this library in any other C++ project.
For instance, it supports Aduino's `String` and `Stream`, but also `std::string`, `std::istream` and `std::ostream`.
Features
--------
@ -55,6 +56,8 @@ double latitude = root["data"][0];
double longitude = root["data"][1];
```
[See JsonParserExample.ino](examples/JsonParserExample/JsonParserExample.ino)
#### Encoding / Generating
```c++
@ -73,6 +76,8 @@ root.printTo(Serial);
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
```
[See JsonGeneratorExample.ino](examples/JsonGeneratorExample/JsonGeneratorExample.ino)
Documentation
-------------

View File

@ -9,8 +9,8 @@
// If you like this project, please add a star!
#include <ArduinoJson.h>
#include <SPI.h>
#include <Ethernet.h>
#include <SPI.h>
EthernetClient client;
@ -36,11 +36,8 @@ void setup() {
void loop() {
if (connect(server)) {
if (sendRequest(server, resource) && skipResponseHeaders()) {
char response[MAX_CONTENT_SIZE];
readReponseContent(response, sizeof(response));
UserData userData;
if (parseUserData(response, &userData)) {
if (readReponseContent(&userData)) {
printUserData(&userData);
}
}
@ -89,7 +86,7 @@ bool sendRequest(const char* host, const char* resource) {
client.print(resource);
client.println(" HTTP/1.0");
client.print("Host: ");
client.println(server);
client.println(host);
client.println("Connection: close");
client.println();
@ -111,13 +108,6 @@ bool skipResponseHeaders() {
return ok;
}
// Read the body of the response from the HTTP server
void readReponseContent(char* content, size_t maxSize) {
size_t length = client.readBytes(content, maxSize);
content[length] = 0;
Serial.println(content);
}
// Parse the JSON from the input string and extract the interesting values
// Here is the JSON we need to parse
// {
@ -143,21 +133,20 @@ void readReponseContent(char* content, size_t maxSize) {
// "bs": "harness real-time e-markets"
// }
// }
bool parseUserData(char* content, struct UserData* userData) {
bool readReponseContent(struct UserData* userData) {
// Compute optimal size of the JSON buffer according to what we need to parse.
// This is only required if you use StaticJsonBuffer.
const size_t BUFFER_SIZE =
JSON_OBJECT_SIZE(8) // the root object has 8 elements
+ JSON_OBJECT_SIZE(5) // the "address" object has 5 elements
+ JSON_OBJECT_SIZE(2) // the "geo" object has 2 elements
+ JSON_OBJECT_SIZE(3); // the "company" object has 3 elements
JSON_OBJECT_SIZE(8) // the root object has 8 elements
+ JSON_OBJECT_SIZE(5) // the "address" object has 5 elements
+ JSON_OBJECT_SIZE(2) // the "geo" object has 2 elements
+ JSON_OBJECT_SIZE(3) // the "company" object has 3 elements
+ MAX_CONTENT_SIZE; // additional space for strings
// Allocate a temporary memory pool on the stack
StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// If the memory pool is too big for the stack, use this instead:
// DynamicJsonBuffer jsonBuffer;
// Allocate a temporary memory pool
DynamicJsonBuffer jsonBuffer(BUFFER_SIZE);
JsonObject& root = jsonBuffer.parseObject(content);
JsonObject& root = jsonBuffer.parseObject(client);
if (!root.success()) {
Serial.println("JSON parsing failed!");

View File

@ -27,6 +27,10 @@
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#endif
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
#endif
// On AVR archiecture, we can use PROGMEM
#ifndef ARDUINOJSON_ENABLE_PROGMEM
#ifdef PROGMEM
@ -106,6 +110,11 @@
#define ARDUINOJSON_ENABLE_STD_STREAM 1
#endif
// on a computer, there is no reason to beleive Arduino Stream is available
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
#endif
#ifndef ARDUINOJSON_ENABLE_ALIGNMENT
// even if not required, most cpu's are faster with aligned pointers
#define ARDUINOJSON_ENABLE_ALIGNMENT 1

View File

@ -1,148 +0,0 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "../Configuration.hpp"
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
#include <WString.h>
#endif
#if ARDUINOJSON_ENABLE_STD_STRING
#include <string>
#endif
namespace ArduinoJson {
namespace Internals {
template <typename TString>
struct StringFuncs {};
template <typename TString>
struct StringFuncs<const TString> : StringFuncs<TString> {};
template <typename TString>
struct StringFuncs<TString&> : StringFuncs<TString> {};
struct CharPtrFuncs {
class Iterator {
const char* _ptr;
public:
Iterator(const char* ptr) : _ptr(ptr ? ptr : "") {}
char next() {
return *_ptr++;
}
};
static bool equals(const char* str, const char* expected) {
return strcmp(str, expected) == 0;
}
template <typename Buffer>
static char* duplicate(const char* str, Buffer* buffer) {
if (!str) return NULL;
size_t size = strlen(str) + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy(dup, str, size);
return static_cast<char*>(dup);
}
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = false;
};
template <>
struct StringFuncs<const char*> : CharPtrFuncs {};
template <>
struct StringFuncs<char*> : CharPtrFuncs {};
template <size_t N>
struct StringFuncs<char[N]> : CharPtrFuncs {};
template <size_t N>
struct StringFuncs<const char[N]> : CharPtrFuncs {};
template <typename TString>
struct StdStringFuncs {
template <typename Buffer>
static char* duplicate(const TString& str, Buffer* buffer) {
if (!str.c_str()) return NULL; // <- Arduino string can return NULL
size_t size = str.length() + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy(dup, str.c_str(), size);
return static_cast<char*>(dup);
}
struct Iterator : CharPtrFuncs::Iterator {
Iterator(const TString& str) : CharPtrFuncs::Iterator(str.c_str()) {}
};
static bool equals(const TString& str, const char* expected) {
return str == expected;
}
static void append(TString& str, char c) {
str += c;
}
static const bool has_append = true;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
template <>
struct StringFuncs<String> : StdStringFuncs<String> {};
template <>
struct StringFuncs<StringSumHelper> : StdStringFuncs<StringSumHelper> {};
#endif
#if ARDUINOJSON_ENABLE_STD_STRING
template <>
struct StringFuncs<std::string> : StdStringFuncs<std::string> {};
#endif
#if ARDUINOJSON_ENABLE_PROGMEM
template <>
struct StringFuncs<const __FlashStringHelper*> {
class Iterator {
const char* _ptr;
public:
Iterator(const __FlashStringHelper* ptr)
: _ptr(reinterpret_cast<const char*>(ptr)) {}
char next() {
return pgm_read_byte_near(_ptr++);
}
};
static bool equals(const __FlashStringHelper* str, const char* expected) {
return strcmp_P(expected, (PGM_P)str) == 0;
}
template <typename Buffer>
static char* duplicate(const __FlashStringHelper* str, Buffer* buffer) {
if (!str) return NULL;
size_t size = strlen_P((PGM_P)str) + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy_P(dup, (PGM_P)str, size);
return static_cast<char*>(dup);
}
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
#endif
}
}

View File

@ -9,8 +9,8 @@
#include "../JsonBuffer.hpp"
#include "../JsonVariant.hpp"
#include "../StringTraits/StringTraits.hpp"
#include "../TypeTraits/EnableIf.hpp"
#include "StringFuncs.hpp"
namespace ArduinoJson {
namespace Internals {

View File

@ -77,7 +77,7 @@ struct JsonParserBuilder {
typedef typename Internals::StringFuncs<TString>::Iterator InputIterator;
typedef JsonParser<StringReader<InputIterator>, TJsonBuffer &> TParser;
static TParser makeParser(TJsonBuffer *buffer, const TString &json,
static TParser makeParser(TJsonBuffer *buffer, TString &json,
uint8_t nestingLimit) {
return TParser(buffer, InputIterator(json), *buffer, nestingLimit);
}
@ -94,9 +94,9 @@ struct JsonParserBuilder<TJsonBuffer, char *> {
}
};
template <typename TJsonBuffer, typename TChar, size_t N>
struct JsonParserBuilder<TJsonBuffer, TChar[N]>
: JsonParserBuilder<TJsonBuffer, TChar *> {};
template <typename TJsonBuffer, size_t N>
struct JsonParserBuilder<TJsonBuffer, char[N]>
: JsonParserBuilder<TJsonBuffer, char *> {};
template <typename TJsonBuffer, typename TString>
inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(

View File

@ -10,10 +10,10 @@
#include "Data/JsonBufferAllocated.hpp"
#include "Data/List.hpp"
#include "Data/ReferenceType.hpp"
#include "Data/StringFuncs.hpp"
#include "Data/ValueSetter.hpp"
#include "JsonVariant.hpp"
#include "Serialization/JsonPrintable.hpp"
#include "StringTraits/StringTraits.hpp"
#include "TypeTraits/ConstRefOrConstPtr.hpp"
#include "TypeTraits/EnableIf.hpp"
#include "TypeTraits/IsFloatingPoint.hpp"

View File

@ -10,10 +10,10 @@
#include "Data/JsonBufferAllocated.hpp"
#include "Data/List.hpp"
#include "Data/ReferenceType.hpp"
#include "Data/StringFuncs.hpp"
#include "Data/ValueSetter.hpp"
#include "JsonPair.hpp"
#include "Serialization/JsonPrintable.hpp"
#include "StringTraits/StringTraits.hpp"
#include "TypeTraits/ConstRefOrConstPtr.hpp"
#include "TypeTraits/EnableIf.hpp"
#include "TypeTraits/IsFloatingPoint.hpp"

View File

@ -7,8 +7,8 @@
#pragma once
#include "Data/StringFuncs.hpp"
#include "JsonVariantBase.hpp"
#include "StringTraits/StringTraits.hpp"
#include "TypeTraits/EnableIf.hpp"
namespace ArduinoJson {

View File

@ -7,8 +7,8 @@
#pragma once
#include "../Data/StringFuncs.hpp"
#include "../Print.hpp"
#include "../StringTraits/StringTraits.hpp"
namespace ArduinoJson {
namespace Internals {

View File

@ -0,0 +1,39 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "../TypeTraits/EnableIf.hpp"
#include "../TypeTraits/IsBaseOf.hpp"
#include "../TypeTraits/RemoveReference.hpp"
namespace ArduinoJson {
namespace Internals {
struct StdStreamFuncs {
class Iterator {
Stream& _stream;
public:
Iterator(Stream& stream) : _stream(stream) {}
char next() {
int n = _stream.read();
return n >= 0 ? static_cast<char>(n) : '\0';
}
};
};
template <typename TStream>
struct StringFuncs<TStream,
// match any type that is derived from std::istream:
typename TypeTraits::EnableIf<TypeTraits::IsBaseOf<
Stream, typename TypeTraits::RemoveReference<
TStream>::type>::value>::type>
: StdStreamFuncs {};
}
}

View File

@ -0,0 +1,57 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
namespace ArduinoJson {
namespace Internals {
struct CharPtrFuncs {
class Iterator {
const char* _ptr;
public:
Iterator(const char* ptr) : _ptr(ptr ? ptr : "") {}
char next() {
char c = *_ptr;
if (c) ++_ptr;
return c;
}
};
static bool equals(const char* str, const char* expected) {
return strcmp(str, expected) == 0;
}
template <typename Buffer>
static char* duplicate(const char* str, Buffer* buffer) {
if (!str) return NULL;
size_t size = strlen(str) + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy(dup, str, size);
return static_cast<char*>(dup);
}
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = false;
};
template <>
struct StringFuncs<const char*, void> : CharPtrFuncs {};
template <>
struct StringFuncs<char*, void> : CharPtrFuncs {};
template <size_t N>
struct StringFuncs<char[N], void> : CharPtrFuncs {};
template <size_t N>
struct StringFuncs<const char[N], void> : CharPtrFuncs {};
}
}

View File

@ -0,0 +1,44 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
namespace ArduinoJson {
namespace Internals {
template <>
struct StringFuncs<const __FlashStringHelper*, void> {
class Iterator {
const char* _ptr;
public:
Iterator(const __FlashStringHelper* ptr)
: _ptr(reinterpret_cast<const char*>(ptr)) {}
char next() {
return pgm_read_byte_near(_ptr++);
}
};
static bool equals(const __FlashStringHelper* str, const char* expected) {
return strcmp_P(expected, (PGM_P)str) == 0;
}
template <typename Buffer>
static char* duplicate(const __FlashStringHelper* str, Buffer* buffer) {
if (!str) return NULL;
size_t size = strlen_P((PGM_P)str) + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy_P(dup, (PGM_P)str, size);
return static_cast<char*>(dup);
}
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
}
}

View File

@ -0,0 +1,42 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include <istream>
#include "../TypeTraits/EnableIf.hpp"
#include "../TypeTraits/IsBaseOf.hpp"
#include "../TypeTraits/RemoveReference.hpp"
namespace ArduinoJson {
namespace Internals {
struct StdStreamFuncs {
class Iterator {
std::istream& _stream;
public:
Iterator(std::istream& stream) : _stream(stream) {}
char next() {
return _stream.eof() ? '\0' : static_cast<char>(_stream.get());
}
private:
Iterator& operator=(const Iterator&); // Visual Studio C4512
};
};
template <typename TStream>
struct StringFuncs<TStream,
// match any type that is derived from std::istream:
typename TypeTraits::EnableIf<TypeTraits::IsBaseOf<
std::istream, typename TypeTraits::RemoveReference<
TStream>::type>::value>::type>
: StdStreamFuncs {};
}
}

View File

@ -0,0 +1,61 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
#include <WString.h>
#endif
#if ARDUINOJSON_ENABLE_STD_STRING
#include <string>
#endif
namespace ArduinoJson {
namespace Internals {
template <typename TString>
struct StdStringFuncs {
template <typename Buffer>
static char* duplicate(const TString& str, Buffer* buffer) {
if (!str.c_str()) return NULL; // <- Arduino string can return NULL
size_t size = str.length() + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy(dup, str.c_str(), size);
return static_cast<char*>(dup);
}
struct Iterator : CharPtrFuncs::Iterator {
Iterator(const TString& str) : CharPtrFuncs::Iterator(str.c_str()) {}
};
static bool equals(const TString& str, const char* expected) {
return str == expected;
}
static void append(TString& str, char c) {
str += c;
}
static const bool has_append = true;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
#if ARDUINOJSON_ENABLE_ARDUINO_STRING
template <>
struct StringFuncs<String, void> : StdStringFuncs<String> {};
template <>
struct StringFuncs<StringSumHelper, void> : StdStringFuncs<StringSumHelper> {};
#endif
#if ARDUINOJSON_ENABLE_STD_STRING
template <>
struct StringFuncs<std::string, void> : StdStringFuncs<std::string> {};
#endif
}
}

View File

@ -0,0 +1,42 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "../Configuration.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename TString, typename Enable = void>
struct StringFuncs {};
template <typename TString>
struct StringFuncs<const TString, void> : StringFuncs<TString> {};
template <typename TString>
struct StringFuncs<TString&, void> : StringFuncs<TString> {};
}
}
#include "CharPointer.hpp"
#if ARDUINOJSON_ENABLE_STD_STRING || ARDUINOJSON_ENABLE_ARDUINO_STRING
#include "StdString.hpp"
#endif
#if ARDUINOJSON_ENABLE_STD_STREAM
#include "StdStream.hpp"
#endif
#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
#include "ArduinoStream.hpp"
#endif
#if ARDUINOJSON_ENABLE_PROGMEM
#include "FlashString.hpp"
#endif

View File

@ -0,0 +1,30 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
namespace ArduinoJson {
namespace TypeTraits {
// A meta-function that returns true if Derived inherits from TBase is an
// integral type.
template <typename TBase, typename TDerived>
class IsBaseOf {
protected: // <- to avoid GCC's "all member functions in class are private"
typedef char Yes[1];
typedef char No[2];
static Yes &probe(const TBase *);
static No &probe(const void *);
public:
enum {
value = sizeof(probe(reinterpret_cast<TDerived *>(0))) == sizeof(Yes)
};
};
}
}

View File

@ -3,5 +3,5 @@
curl https://cmake.org/files/v3.4/cmake-3.4.0-Linux-x86_64.tar.gz | tar xz -C /tmp --strip 1
/tmp/bin/cmake -DSANITIZE=true .
make
make test
cmake --build .
ctest -VV .

View File

@ -351,6 +351,16 @@ TEST_F(JsonParser_Array_Tests, UnfinishedCComment) {
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, EndsInCppComment) {
whenInputIs("[//COMMENT");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, AfterClosingStar) {
whenInputIs("[/*COMMENT*");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, DeeplyNested) {
whenInputIs("[[[[[[[[[[[[[[[[[[[\"Not too deep\"]]]]]]]]]]]]]]]]]]]");
parseMustSucceed();

View File

@ -5,25 +5,25 @@
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#include <sstream>
#include <gtest/gtest.h>
#include <ArduinoJson.h>
#include <gtest/gtest.h>
#include <sstream>
TEST(StdStream, JsonVariantFalse) {
TEST(StdStream_Tests, JsonVariantFalse) {
std::ostringstream os;
JsonVariant variant = false;
os << variant;
ASSERT_EQ("false", os.str());
}
TEST(StdStream, JsonVariantString) {
TEST(StdStream_Tests, JsonVariantString) {
std::ostringstream os;
JsonVariant variant = "coucou";
os << variant;
ASSERT_EQ("\"coucou\"", os.str());
}
TEST(StdStream, JsonObject) {
TEST(StdStream_Tests, JsonObject) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
@ -32,7 +32,7 @@ TEST(StdStream, JsonObject) {
ASSERT_EQ("{\"key\":\"value\"}", os.str());
}
TEST(StdStream, JsonObjectSubscript) {
TEST(StdStream_Tests, JsonObjectSubscript) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
@ -41,7 +41,7 @@ TEST(StdStream, JsonObjectSubscript) {
ASSERT_EQ("\"value\"", os.str());
}
TEST(StdStream, JsonArray) {
TEST(StdStream_Tests, JsonArray) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
@ -50,7 +50,7 @@ TEST(StdStream, JsonArray) {
ASSERT_EQ("[\"value\"]", os.str());
}
TEST(StdStream, JsonArraySubscript) {
TEST(StdStream_Tests, JsonArraySubscript) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
@ -58,3 +58,21 @@ TEST(StdStream, JsonArraySubscript) {
os << array[0];
ASSERT_EQ("\"value\"", os.str());
}
TEST(StdStream_Tests, ParseArray) {
std::istringstream json("[42]");
DynamicJsonBuffer jsonBuffer;
JsonArray& arr = jsonBuffer.parseArray(json);
ASSERT_TRUE(arr.success());
ASSERT_EQ(1, arr.size());
ASSERT_EQ(42, arr[0]);
}
TEST(StdStream_Tests, ParseObject) {
std::istringstream json("{hello:world}");
DynamicJsonBuffer jsonBuffer;
JsonObject& obj = jsonBuffer.parseObject(json);
ASSERT_TRUE(obj.success());
ASSERT_EQ(1, obj.size());
ASSERT_STREQ("world", obj["hello"]);
}

17
test/TypeTraits_Tests.cpp Normal file
View File

@ -0,0 +1,17 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#include <ArduinoJson.h>
#include <gtest/gtest.h>
#include <sstream>
using namespace ArduinoJson::TypeTraits;
TEST(StdStream, IsBaseOf) {
ASSERT_FALSE((IsBaseOf<std::istream, std::ostringstream>::value));
ASSERT_TRUE((IsBaseOf<std::istream, std::istringstream>::value));
}