Explorar el Código

Store parsed token as string and allow conversion between various types (issues #64, #69, #90, #93)

Benoit Blanchon hace 10 años
padre
commit
ef2641b49b

+ 2 - 0
CHANGELOG.md

@@ -9,6 +9,8 @@ v5.0 (currently in beta)
 * Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87)
 * Added support of non standard JSON input (issue #44)
 * Added support of comments in JSON input (issue #88)
+* Added implicit cast between numerical types (issues #64, #69, #93)
+* Added ability to read number values as string (issue #90)
 * Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66)
 * Switched to new the library layout (requires Arduino 1.0.6 or above)
 

+ 2 - 2
README.md

@@ -12,7 +12,7 @@ It has been written with Arduino in mind, but it isn't linked to Arduino librari
 Features
 --------
 
-* JSON decoding
+* JSON decoding (comments are supported)
 * JSON encoding (with optional indentation)
 * Elegant API, very easy to use 
 * Efficient (no malloc, nor copy)
@@ -80,4 +80,4 @@ From GitHub user `zacsketches`:
 
 ---
 
-Found this library useful? [Help me back with a donation!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donate%40benoitblanchon%2efr&lc=GB&item_name=Benoit%20Blanchon&item_number=Arduino%20JSON&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) :smile:
+Found this library useful? [Help me back with a donation!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donate%40benoitblanchon%2efr&lc=GB&item_name=Benoit%20Blanchon&item_number=Arduino%20JSON&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) :smile:

+ 1 - 1
include/ArduinoJson/Arduino/Print.hpp

@@ -11,7 +11,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-// This class reproduces Arduino's Print
+// This class reproduces Arduino's Print class
 class Print {
  public:
   virtual ~Print() {}

+ 14 - 1
include/ArduinoJson/Arduino/String.hpp

@@ -10,7 +10,20 @@
 
 #include <string>
 
-typedef std::string String;
+// This class reproduces Arduino's String class
+class String : public std::string {
+ public:
+  String(const char *cstr = "") : std::string(cstr) {}
+  String(const String &str) : std::string(str) {}
+  explicit String(char c);
+  explicit String(unsigned char);
+  explicit String(int);
+  explicit String(unsigned int);
+  explicit String(long);
+  explicit String(unsigned long);
+  explicit String(float, unsigned char decimalPlaces = 2);
+  explicit String(double, unsigned char decimalPlaces = 2);
+};
 
 #else
 

+ 29 - 0
include/ArduinoJson/Internals/DynamicStringBuilder.hpp

@@ -0,0 +1,29 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#pragma once
+
+#include "../Arduino/Print.hpp"
+#include "../Arduino/String.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A Print implementation that allows to write in a String
+class DynamicStringBuilder : public Print {
+ public:
+  DynamicStringBuilder(String &str) : _str(str) {}
+
+  virtual size_t write(uint8_t c) {
+    _str += c;
+    return 1;
+  }
+
+ private:
+  String &_str;
+};
+}
+}

+ 0 - 4
include/ArduinoJson/Internals/JsonParser.hpp

@@ -28,16 +28,12 @@ class JsonParser {
 
  private:
   bool skip(char charToSkip);
-  bool skip(const char *wordToSkip);
 
   const char *parseString();
   bool parseAnythingTo(JsonVariant *destination);
   FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
 
   inline bool parseArrayTo(JsonVariant *destination);
-  inline bool parseBooleanTo(JsonVariant *destination);
-  inline bool parseNullTo(JsonVariant *destination);
-  inline bool parseNumberTo(JsonVariant *destination);
   inline bool parseObjectTo(JsonVariant *destination);
   inline bool parseStringTo(JsonVariant *destination);
 

+ 18 - 8
include/ArduinoJson/Internals/JsonPrintable.hpp

@@ -10,7 +10,8 @@
 #include "IndentedPrint.hpp"
 #include "JsonWriter.hpp"
 #include "Prettyfier.hpp"
-#include "StringBuilder.hpp"
+#include "StaticStringBuilder.hpp"
+#include "DynamicStringBuilder.hpp"
 
 #ifdef ARDUINOJSON_ENABLE_STD_STREAM
 #include "StreamPrintAdapter.hpp"
@@ -33,15 +34,20 @@ class JsonPrintable {
   }
 
 #ifdef ARDUINOJSON_ENABLE_STD_STREAM
-  std::ostream& printTo(std::ostream &os) const {
+  std::ostream &printTo(std::ostream &os) const {
     StreamPrintAdapter adapter(os);
     printTo(adapter);
     return os;
   }
-#endif 
+#endif
 
   size_t printTo(char *buffer, size_t bufferSize) const {
-    StringBuilder sb(buffer, bufferSize);
+    StaticStringBuilder sb(buffer, bufferSize);
+    return printTo(sb);
+  }
+
+  size_t printTo(String &str) const {
+    DynamicStringBuilder sb(str);
     return printTo(sb);
   }
 
@@ -51,7 +57,7 @@ class JsonPrintable {
   }
 
   size_t prettyPrintTo(char *buffer, size_t bufferSize) const {
-    StringBuilder sb(buffer, bufferSize);
+    StaticStringBuilder sb(buffer, bufferSize);
     return prettyPrintTo(sb);
   }
 
@@ -60,6 +66,11 @@ class JsonPrintable {
     return prettyPrintTo(indentedPrint);
   }
 
+  size_t prettyPrintTo(String &str) const {
+    DynamicStringBuilder sb(str);
+    return prettyPrintTo(sb);
+  }
+
   size_t measureLength() const {
     DummyPrint dp;
     return printTo(dp);
@@ -75,11 +86,10 @@ class JsonPrintable {
 };
 
 #ifdef ARDUINOJSON_ENABLE_STD_STREAM
-template<typename T>
-inline std::ostream& operator<<(std::ostream& os, const JsonPrintable<T>& v) {
+template <typename T>
+inline std::ostream &operator<<(std::ostream &os, const JsonPrintable<T> &v) {
   return v.printTo(os);
 }
 #endif
-
 }
 }

+ 1 - 7
include/ArduinoJson/Internals/JsonVariantContent.hpp

@@ -17,17 +17,11 @@ namespace Internals {
 // A union that defines the actual content of a JsonVariant.
 // The enum JsonVariantType determines which member is in use.
 union JsonVariantContent {
-  bool asBoolean;
   double asDouble;       // asDouble is also used for float
-  long asLong;           // asLong is also used for char, short and int
+  long asLong;           // asLong is also used for bool, char, short and int
   const char* asString;  // asString can be null
   JsonArray* asArray;    // asArray cannot be null
   JsonObject* asObject;  // asObject cannot be null
-
-  template <typename T>
-  T as() const;
 };
 }
 }
-
-#include "JsonVariantContent.ipp"

+ 0 - 96
include/ArduinoJson/Internals/JsonVariantContent.ipp

@@ -1,96 +0,0 @@
-// Copyright Benoit Blanchon 2014-2015
-// MIT License
-//
-// Arduino JSON library
-// https://github.com/bblanchon/ArduinoJson
-
-#pragma once
-
-namespace ArduinoJson {
-
-// Forward declarations
-class JsonArray;
-class JsonObject;
-
-namespace Internals {
-template <>
-inline bool JsonVariantContent::as<bool>() const {
-  return asBoolean;
-}
-
-template <>
-inline char const* JsonVariantContent::as<char const*>() const {
-  return asString;
-}
-
-template <>
-inline double JsonVariantContent::as<double>() const {
-  return asDouble;
-}
-
-template <>
-inline float JsonVariantContent::as<float>() const {
-  return static_cast<float>(asDouble);
-}
-
-template <>
-inline JsonArray& JsonVariantContent::as<JsonArray&>() const {
-  return *asArray;
-}
-
-template <>
-inline const JsonArray& JsonVariantContent::as<JsonArray const&>() const {
-  return *asArray;
-}
-
-template <>
-inline JsonObject& JsonVariantContent::as<JsonObject&>() const {
-  return *asObject;
-}
-
-template <>
-inline const JsonObject& JsonVariantContent::as<JsonObject const&>() const {
-  return *asObject;
-}
-
-template <>
-inline signed char JsonVariantContent::as<signed char>() const {
-  return static_cast<signed char>(asLong);
-}
-
-template <>
-inline signed int JsonVariantContent::as<signed int>() const {
-  return static_cast<signed int>(asLong);
-}
-
-template <>
-inline signed long JsonVariantContent::as<signed long>() const {
-  return static_cast<signed long>(asLong);
-}
-
-template <>
-inline signed short JsonVariantContent::as<signed short>() const {
-  return static_cast<signed short>(asLong);
-}
-
-template <>
-inline unsigned char JsonVariantContent::as<unsigned char>() const {
-  return static_cast<unsigned char>(asLong);
-}
-
-template <>
-inline unsigned int JsonVariantContent::as<unsigned int>() const {
-  return static_cast<unsigned int>(asLong);
-}
-
-template <>
-inline unsigned long JsonVariantContent::as<unsigned long>() const {
-  return static_cast<unsigned long>(asLong);
-}
-
-template <>
-inline unsigned short JsonVariantContent::as<unsigned short>() const {
-  return static_cast<unsigned short>(asLong);
-}
-}
-}

+ 4 - 3
include/ArduinoJson/Internals/JsonVariantType.hpp

@@ -16,11 +16,12 @@ namespace Internals {
 // The value determines which member of JsonVariantContent is used.
 enum JsonVariantType {
   JSON_UNDEFINED,  // the JsonVariant has not been initialized
-  JSON_ARRAY,      // the JsonVariant stores a pointer to a JsonArray
-  JSON_OBJECT,     // the JsonVariant stores a pointer to a JsonObject
-  JSON_BOOLEAN,    // the JsonVariant stores a bool
+  JSON_UNPARSED,   // the JsonVariant contains an unparsed string
   JSON_STRING,     // the JsonVariant stores a const char*
+  JSON_BOOLEAN,    // the JsonVariant stores a bool
   JSON_LONG,       // the JsonVariant stores a long
+  JSON_ARRAY,      // the JsonVariant stores a pointer to a JsonArray
+  JSON_OBJECT,     // the JsonVariant stores a pointer to a JsonObject
 
   // The following values are reserved for double values
   // Multiple values are used for double, depending on the number of decimal

+ 4 - 4
include/ArduinoJson/Internals/JsonWriter.hpp

@@ -37,10 +37,8 @@ class JsonWriter {
   void writeColon() { write(':'); }
   void writeComma() { write(','); }
 
-  void writeBoolean(bool value) {
-    write(value ? "true" : "false");
-  }
-  
+  void writeBoolean(bool value) { write(value ? "true" : "false"); }
+
   void writeString(const char *value) {
     if (!value) {
       write("null");
@@ -67,6 +65,8 @@ class JsonWriter {
     _length += _sink.print(value, decimals);
   }
 
+  void writeRaw(const char *s) { return write(s); }
+
  protected:
   void write(char c) { _length += _sink.write(c); }
   void write(const char *s) { _length += _sink.print(s); }

+ 2 - 2
include/ArduinoJson/Internals/StringBuilder.hpp → include/ArduinoJson/Internals/StaticStringBuilder.hpp

@@ -12,9 +12,9 @@ namespace ArduinoJson {
 namespace Internals {
 
 // A Print implementation that allows to write in a char[]
-class StringBuilder : public Print {
+class StaticStringBuilder : public Print {
  public:
-  StringBuilder(char *buf, int size)
+  StaticStringBuilder(char *buf, int size)
       : buffer(buf), capacity(size - 1), length(0) {
     buffer[0] = '\0';
   }

+ 20 - 0
include/ArduinoJson/Internals/Unparsed.hpp

@@ -0,0 +1,20 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+class Unparsed {
+ public:
+  explicit Unparsed(const char* str) : _str(str) {}
+  operator const char*() const { return _str; }
+
+ private:
+  const char* _str;
+};
+}
+}

+ 12 - 0
include/ArduinoJson/JsonArray.ipp

@@ -202,4 +202,16 @@ template <>
 inline JsonArray const &JsonVariant::invalid<JsonArray const &>() {
   return JsonArray::invalid();
 }
+
+template <>
+inline JsonArray &JsonVariant::as<JsonArray &>() const {
+  if (_type == Internals::JSON_ARRAY) return *_content.asArray;
+  return JsonArray::invalid();
+}
+
+template <>
+inline const JsonArray &JsonVariant::as<const JsonArray &>() const {
+  if (_type == Internals::JSON_ARRAY) return *_content.asArray;
+  return JsonArray::invalid();
+}
 }

+ 12 - 0
include/ArduinoJson/JsonObject.ipp

@@ -217,4 +217,16 @@ template <>
 inline JsonObject &JsonVariant::invalid<JsonObject &>() {
   return JsonObject::invalid();
 }
+
+template <>
+inline JsonObject &JsonVariant::as<JsonObject &>() const {
+  if (_type == Internals::JSON_OBJECT) return *_content.asObject;
+  return JsonObject::invalid();
+}
+
+template <>
+inline const JsonObject &JsonVariant::as<const JsonObject &>() const {
+  if (_type == Internals::JSON_OBJECT) return *_content.asObject;
+  return JsonObject::invalid();
+}
 }

+ 6 - 2
include/ArduinoJson/JsonVariant.hpp

@@ -12,6 +12,7 @@
 #include "Internals/JsonPrintable.hpp"
 #include "Internals/JsonVariantContent.hpp"
 #include "Internals/JsonVariantType.hpp"
+#include "Internals/Unparsed.hpp"
 #include "JsonVariantBase.hpp"
 
 namespace ArduinoJson {
@@ -55,6 +56,9 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   // Create a JsonVariant containing a string.
   FORCE_INLINE JsonVariant(const char *value);
 
+  // Create a JsonVariant containing an unparsed string
+  FORCE_INLINE JsonVariant(Internals::Unparsed value);
+
   // Create a JsonVariant containing a reference to an array.
   FORCE_INLINE JsonVariant(JsonArray &array);
 
@@ -64,12 +68,12 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   // Get the variant as the specified type.
   // See cast operators for details.
   template <typename T>
-  FORCE_INLINE T as() const;
+  T as() const;
 
   // Tells weither the variant has the specified type.
   // Returns true if the variant has type type T, false otherwise.
   template <typename T>
-  FORCE_INLINE bool is() const;
+  bool is() const;
 
   // Serialize the variant to a JsonWriter
   void writeTo(Internals::JsonWriter &writer) const;

+ 75 - 22
include/ArduinoJson/JsonVariant.ipp

@@ -12,7 +12,7 @@ namespace ArduinoJson {
 
 inline JsonVariant::JsonVariant(bool value) {
   _type = Internals::JSON_BOOLEAN;
-  _content.asBoolean = value;
+  _content.asLong = value;
 }
 
 inline JsonVariant::JsonVariant(const char *value) {
@@ -20,6 +20,11 @@ inline JsonVariant::JsonVariant(const char *value) {
   _content.asString = value;
 }
 
+inline JsonVariant::JsonVariant(Internals::Unparsed value) {
+  _type = Internals::JSON_UNPARSED;
+  _content.asString = value;
+}
+
 inline JsonVariant::JsonVariant(double value, uint8_t decimals) {
   _type = static_cast<Internals::JsonVariantType>(
       Internals::JSON_DOUBLE_0_DECIMALS + decimals);
@@ -82,9 +87,61 @@ inline JsonVariant::JsonVariant(unsigned short value) {
   _content.asLong = value;
 }
 
-template <typename T>
-inline T JsonVariant::as() const {
-  return is<T>() ? _content.as<T>() : invalid<T>();
+template <>
+double JsonVariant::as<double>() const;
+
+template <>
+long JsonVariant::as<long>() const;
+
+template <>
+String JsonVariant::as<String>() const;
+
+template <>
+const char *JsonVariant::as<const char *>() const;
+
+template <>
+inline bool JsonVariant::as<bool>() const {
+  return as<long>();
+}
+
+template <>
+inline signed char JsonVariant::as<signed char>() const {
+  return static_cast<signed char>(as<long>());
+}
+
+template <>
+inline unsigned char JsonVariant::as<unsigned char>() const {
+  return static_cast<unsigned char>(as<long>());
+}
+
+template <>
+inline signed short JsonVariant::as<signed short>() const {
+  return static_cast<signed short>(as<long>());
+}
+
+template <>
+inline unsigned short JsonVariant::as<unsigned short>() const {
+  return static_cast<unsigned short>(as<long>());
+}
+
+template <>
+inline signed int JsonVariant::as<signed int>() const {
+  return static_cast<signed int>(as<long>());
+}
+
+template <>
+inline unsigned int JsonVariant::as<unsigned int>() const {
+  return static_cast<unsigned int>(as<long>());
+}
+
+template <>
+inline unsigned long JsonVariant::as<unsigned long>() const {
+  return static_cast<unsigned long>(as<long>());
+}
+
+template <>
+inline float JsonVariant::as<float>() const {
+  return static_cast<float>(as<double>());
 }
 
 template <typename T>
@@ -97,6 +154,12 @@ inline bool JsonVariant::is() const {
   return false;
 }
 
+template <>  // in .cpp
+bool JsonVariant::is<signed long>() const;
+
+template <>  // in .cpp
+bool JsonVariant::is<double>() const;
+
 template <>
 inline bool JsonVariant::is<bool>() const {
   return _type == Internals::JSON_BOOLEAN;
@@ -107,14 +170,9 @@ inline bool JsonVariant::is<char const *>() const {
   return _type == Internals::JSON_STRING;
 }
 
-template <>
-inline bool JsonVariant::is<double>() const {
-  return _type >= Internals::JSON_DOUBLE_0_DECIMALS;
-}
-
 template <>
 inline bool JsonVariant::is<float>() const {
-  return _type >= Internals::JSON_DOUBLE_0_DECIMALS;
+  return is<double>();
 }
 
 template <>
@@ -139,42 +197,37 @@ inline bool JsonVariant::is<JsonObject const &>() const {
 
 template <>
 inline bool JsonVariant::is<signed char>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<signed int>() const {
-  return _type == Internals::JSON_LONG;
-}
-
-template <>
-inline bool JsonVariant::is<signed long>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<signed short>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<unsigned char>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<unsigned int>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<unsigned long>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 template <>
 inline bool JsonVariant::is<unsigned short>() const {
-  return _type == Internals::JSON_LONG;
+  return is<signed long>();
 }
 
 #ifdef ARDUINOJSON_ENABLE_STD_STREAM

+ 31 - 0
src/Arduino/String.cpp

@@ -0,0 +1,31 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#ifndef ARDUINO
+
+#include "../../include/ArduinoJson/Arduino/String.hpp"
+
+#include <stdio.h>  // for sprintf()
+
+String::String(double value, unsigned char digits) {
+  char tmp[32];
+  sprintf(tmp, "%.*f", digits, value);
+  *this = tmp;
+}
+
+String::String(int value) {
+  char tmp[32];
+  sprintf(tmp, "%d", value);
+  *this = tmp;
+}
+
+String::String(long value) {
+  char tmp[32];
+  sprintf(tmp, "%ld", value);
+  *this = tmp;
+}
+
+#endif

+ 12 - 81
src/Internals/JsonParser.cpp

@@ -6,9 +6,6 @@
 
 #include "../../include/ArduinoJson/Internals/JsonParser.hpp"
 
-#include <stdlib.h>  // for strtol, strtod
-#include <ctype.h>
-
 #include "../../include/ArduinoJson/Internals/Comments.hpp"
 #include "../../include/ArduinoJson/Internals/Encoding.hpp"
 #include "../../include/ArduinoJson/JsonArray.hpp"
@@ -26,17 +23,6 @@ bool JsonParser::skip(char charToSkip) {
   return true;
 }
 
-bool JsonParser::skip(const char *wordToSkip) {
-  register const char *ptr = _readPtr;
-  while (*wordToSkip && *ptr == *wordToSkip) {
-    wordToSkip++;
-    ptr++;
-  }
-  if (*wordToSkip != '\0') return false;
-  _readPtr = ptr;
-  return true;
-}
-
 bool JsonParser::parseAnythingTo(JsonVariant *destination) {
   if (_nestingLimit == 0) return false;
   _nestingLimit--;
@@ -55,27 +41,6 @@ inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) {
     case '{':
       return parseObjectTo(destination);
 
-    case 't':
-    case 'f':
-      return parseBooleanTo(destination);
-
-    case '-':
-    case '.':
-    case '0':
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      return parseNumberTo(destination);
-
-    case 'n':
-      return parseNullTo(destination);
-
     default:
       return parseStringTo(destination);
   }
@@ -166,64 +131,24 @@ bool JsonParser::parseObjectTo(JsonVariant *destination) {
   return true;
 }
 
-bool JsonParser::parseBooleanTo(JsonVariant *destination) {
-  if (skip("true")) {
-    *destination = true;
-    return true;
-  } else if (skip("false")) {
-    *destination = false;
-    return true;
-  } else {
-    return false;
-  }
-}
-
-bool JsonParser::parseNumberTo(JsonVariant *destination) {
-  char *endOfLong;
-  long longValue = strtol(_readPtr, &endOfLong, 10);
-  char stopChar = *endOfLong;
-
-  // Could it be a floating point value?
-  bool couldBeFloat = stopChar == '.' || stopChar == 'e' || stopChar == 'E';
-
-  if (couldBeFloat) {
-    // Yes => parse it as a double
-    double doubleValue = strtod(_readPtr, const_cast<char **>(&_readPtr));
-    // Count the decimal digits
-    uint8_t decimals = static_cast<uint8_t>(_readPtr - endOfLong - 1);
-    // Set the variant as a double
-    *destination = JsonVariant(doubleValue, decimals);
-  } else {
-    // No => set the variant as a long
-    _readPtr = endOfLong;
-    *destination = longValue;
-  }
-  return true;
-}
-
-bool JsonParser::parseNullTo(JsonVariant *destination) {
-  const char *NULL_STRING = NULL;
-  if (!skip("null")) return false;
-  *destination = NULL_STRING;
-  return true;
-}
-
 static inline bool isInRange(char c, char min, char max) {
   return min <= c && c <= max;
 }
 
 static inline bool isLetterOrNumber(char c) {
   return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') ||
-         isInRange(c, 'A', 'Z');
+         isInRange(c, 'A', 'Z') || c == '-' || c == '.';
 }
 
+static inline bool isQuote(char c) { return c == '\'' || c == '\"'; }
+
 const char *JsonParser::parseString() {
   const char *readPtr = _readPtr;
   char *writePtr = _writePtr;
 
   char c = *readPtr;
 
-  if (c == '\'' || c == '\"') {  // quotes
+  if (isQuote(c)) {  // quotes
     char stopChar = c;
     for (;;) {
       c = *++readPtr;
@@ -263,7 +188,13 @@ const char *JsonParser::parseString() {
 }
 
 bool JsonParser::parseStringTo(JsonVariant *destination) {
+  bool hasQuotes = isQuote(_readPtr[0]);
   const char *value = parseString();
-  *destination = value;
-  return value != NULL;
+  if (value == NULL) return false;
+  if (hasQuotes) {
+    *destination = value;
+  } else {
+    *destination = Unparsed(value);
+  }
+  return true;
 }

+ 2 - 2
src/Internals/StringBuilder.cpp → src/Internals/StaticStringBuilder.cpp

@@ -4,11 +4,11 @@
 // Arduino JSON library
 // https://github.com/bblanchon/ArduinoJson
 
-#include "../../include/ArduinoJson/Internals/StringBuilder.hpp"
+#include "../../include/ArduinoJson/Internals/StaticStringBuilder.hpp"
 
 using namespace ArduinoJson::Internals;
 
-size_t StringBuilder::write(uint8_t c) {
+size_t StaticStringBuilder::write(uint8_t c) {
   if (length >= capacity) return 0;
 
   buffer[length++] = c;

+ 1 - 1
src/JsonObject.cpp

@@ -8,7 +8,7 @@
 
 #include <string.h>  // for strcmp
 
-#include "../include/ArduinoJson/Internals/StringBuilder.hpp"
+#include "../include/ArduinoJson/Internals/StaticStringBuilder.hpp"
 #include "../include/ArduinoJson/JsonArray.hpp"
 #include "../include/ArduinoJson/JsonBuffer.hpp"
 

+ 102 - 13
src/JsonVariant.cpp

@@ -9,22 +9,111 @@
 #include "../include/ArduinoJson/JsonArray.hpp"
 #include "../include/ArduinoJson/JsonObject.hpp"
 
-using namespace ArduinoJson;
+#include <errno.h>   // for errno
+#include <stdlib.h>  // for strtol, strtod
+
 using namespace ArduinoJson::Internals;
 
+namespace ArduinoJson {
+
+template <>
+const char *JsonVariant::as<const char *>() const {
+  if (_type == JSON_UNPARSED && _content.asString &&
+      !strcmp("null", _content.asString))
+    return NULL;
+  if (_type == JSON_STRING || _type == JSON_UNPARSED) return _content.asString;
+  return NULL;
+}
+
+template <>
+double JsonVariant::as<double>() const {
+  if (_type >= JSON_DOUBLE_0_DECIMALS) return _content.asDouble;
+
+  if (_type == JSON_LONG || _type == JSON_BOOLEAN)
+    return static_cast<double>(_content.asLong);
+
+  if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString)
+    return strtod(_content.asString, NULL);
+
+  return 0.0;
+}
+
+template <>
+long JsonVariant::as<long>() const {
+  if (_type == JSON_LONG || _type == JSON_BOOLEAN) return _content.asLong;
+
+  if (_type >= JSON_DOUBLE_0_DECIMALS)
+    return static_cast<long>(_content.asDouble);
+
+  if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) {
+    if (!strcmp("true", _content.asString)) return 1;
+    return strtol(_content.asString, NULL, 10);
+  }
+
+  return 0L;
+}
+
+template <>
+String JsonVariant::as<String>() const {
+  if ((_type == JSON_STRING || _type == JSON_UNPARSED) &&
+      _content.asString != NULL)
+    return String(_content.asString);
+
+  if (_type == JSON_LONG || _type == JSON_BOOLEAN)
+    return String(_content.asLong);
+
+  if (_type >= JSON_DOUBLE_0_DECIMALS) {
+    uint8_t decimals = static_cast<uint8_t>(_type - JSON_DOUBLE_0_DECIMALS);
+    return String(_content.asDouble, decimals);
+  }
+
+  String s;
+  printTo(s);
+  return s;
+}
+
+template <>
+bool JsonVariant::is<signed long>() const {
+  if (_type == JSON_LONG) return true;
+
+  if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
+
+  char *end;
+  errno = 0;
+  strtol(_content.asString, &end, 10);
+
+  return *end == '\0' && errno == 0;
+}
+
+template <>
+bool JsonVariant::is<double>() const {
+  if (_type >= JSON_DOUBLE_0_DECIMALS) return true;
+
+  if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
+
+  char *end;
+  errno = 0;
+  strtod(_content.asString, &end);
+
+  return *end == '\0' && errno == 0 && !is<long>();
+}
+
 void JsonVariant::writeTo(JsonWriter &writer) const {
-  if (is<const JsonArray &>())
-    as<const JsonArray &>().writeTo(writer);
-  else if (is<const JsonObject &>())
-    as<const JsonObject &>().writeTo(writer);
-  else if (is<const char *>())
-    writer.writeString(as<const char *>());
-  else if (is<long>())
-    writer.writeLong(as<long>());
-  else if (is<bool>())
-    writer.writeBoolean(as<bool>());
-  else if (is<double>()) {
+  if (_type == JSON_ARRAY) _content.asArray->writeTo(writer);
+
+  if (_type == JSON_OBJECT) _content.asObject->writeTo(writer);
+
+  if (_type == JSON_STRING) writer.writeString(_content.asString);
+
+  if (_type == JSON_UNPARSED) writer.writeRaw(_content.asString);
+
+  if (_type == JSON_LONG) writer.writeLong(_content.asLong);
+
+  if (_type == JSON_BOOLEAN) writer.writeBoolean(_content.asLong);
+
+  if (_type >= JSON_DOUBLE_0_DECIMALS) {
     uint8_t decimals = static_cast<uint8_t>(_type - JSON_DOUBLE_0_DECIMALS);
-    writer.writeDouble(as<double>(), decimals);
+    writer.writeDouble(_content.asDouble, decimals);
   }
 }
+}

+ 34 - 0
test/ArduinoString_Tests.cpp

@@ -164,3 +164,37 @@ TEST_F(ArduinoStringTests, JsonArraySubscript) {
   eraseString(value);
   ASSERT_STREQ("world", array[0]);
 }
+
+TEST_F(ArduinoStringTests, JsonArray_PrintTo) {
+  JsonArray &array = _jsonBuffer.createArray();
+  array.add(4);
+  array.add(2);
+  String json;
+  array.printTo(json);
+  ASSERT_EQ(String("[4,2]"), json);
+}
+
+TEST_F(ArduinoStringTests, JsonArray_PrettyPrintTo) {
+  JsonArray &array = _jsonBuffer.createArray();
+  array.add(4);
+  array.add(2);
+  String json;
+  array.prettyPrintTo(json);
+  ASSERT_EQ(String("[\r\n  4,\r\n  2\r\n]"), json);
+}
+
+TEST_F(ArduinoStringTests, JsonObject_PrintTo) {
+  JsonObject &object = _jsonBuffer.createObject();
+  object["key"] = "value";
+  String json;
+  object.printTo(json);
+  ASSERT_EQ(String("{\"key\":\"value\"}"), json);
+}
+
+TEST_F(ArduinoStringTests, JsonObject_PrettyPrintTo) {
+  JsonObject &object = _jsonBuffer.createObject();
+  object["key"] = "value";
+  String json;
+  object.prettyPrintTo(json);
+  ASSERT_EQ(String("{\r\n  \"key\": \"value\"\r\n}"), json);
+}

+ 28 - 0
test/Issue90.cpp

@@ -0,0 +1,28 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#include <gtest/gtest.h>
+#include <limits.h>  // for LONG_MAX
+#define ARDUINOJSON_ENABLE_STD_STREAM
+#include <ArduinoJson.h>
+
+#define SUITE Issue90
+
+using namespace ArduinoJson::Internals;
+
+static const char* superLong =
+    "12345678901234567890123456789012345678901234567890123456789012345678901234"
+    "5678901234567890123456789012345678901234567890123456789012345678901234567";
+
+static const JsonVariant variant = Unparsed(superLong);
+
+TEST(SUITE, IsNotALong) { ASSERT_FALSE(variant.is<long>()); }
+
+TEST(SUITE, AsLong) { ASSERT_EQ(LONG_MAX, variant.as<long>()); }
+
+TEST(SUITE, IsAString) { ASSERT_FALSE(variant.is<const char*>()); }
+
+TEST(SUITE, AsString) { ASSERT_STREQ(superLong, variant.as<const char*>()); }

+ 0 - 20
test/JsonParser_Array_Tests.cpp

@@ -144,26 +144,6 @@ TEST_F(JsonParser_Array_Tests, TwoNulls) {
   secondElementMustBe(nullCharPtr);
 }
 
-TEST_F(JsonParser_Array_Tests, IncompleteNull) {
-  whenInputIs("[nul!]");
-  parseMustFail();
-}
-
-TEST_F(JsonParser_Array_Tests, IncompleteTrue) {
-  whenInputIs("[tru!]");
-  parseMustFail();
-}
-
-TEST_F(JsonParser_Array_Tests, IncompleteFalse) {
-  whenInputIs("[fals!]");
-  parseMustFail();
-}
-
-TEST_F(JsonParser_Array_Tests, MixedTrueFalse) {
-  whenInputIs("[trufalse]");
-  parseMustFail();
-}
-
 TEST_F(JsonParser_Array_Tests, TwoStringsDoubleQuotes) {
   whenInputIs("[ \"hello\" , \"world\" ]");
 

+ 177 - 0
test/JsonVariant_As_Tests.cpp

@@ -0,0 +1,177 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#include <gtest/gtest.h>
+#define ARDUINOJSON_ENABLE_STD_STREAM
+#include <ArduinoJson.h>
+
+static const char* null = 0;
+
+TEST(JsonVariant_As_Tests, DoubleAsBool) {
+  JsonVariant variant = 4.2;
+  ASSERT_TRUE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, DoubleAsCstr) {
+  JsonVariant variant = 4.2;
+  ASSERT_FALSE(variant.as<const char*>());
+}
+
+TEST(JsonVariant_As_Tests, DoubleAsString) {
+  JsonVariant variant = 4.2;
+  ASSERT_EQ(String("4.20"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, DoubleAsLong) {
+  JsonVariant variant = 4.2;
+  ASSERT_EQ(4L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, DoubleZeroAsBool) {
+  JsonVariant variant = 0.0;
+  ASSERT_FALSE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, DoubleZeroAsLong) {
+  JsonVariant variant = 0.0;
+  ASSERT_EQ(0L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, FalseAsBool) {
+  JsonVariant variant = false;
+  ASSERT_FALSE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, FalseAsDouble) {
+  JsonVariant variant = false;
+  ASSERT_EQ(0.0, variant.as<double>());
+}
+
+TEST(JsonVariant_As_Tests, FalseAsLong) {
+  JsonVariant variant = false;
+  ASSERT_EQ(0L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, FalseAsString) {
+  JsonVariant variant = false;
+  ASSERT_EQ(String("0"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, TrueAsBool) {
+  JsonVariant variant = true;
+  ASSERT_TRUE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, TrueAsDouble) {
+  JsonVariant variant = true;
+  ASSERT_EQ(1.0, variant.as<double>());
+}
+
+TEST(JsonVariant_As_Tests, TrueAsLong) {
+  JsonVariant variant = true;
+  ASSERT_EQ(1L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, TrueAsString) {
+  JsonVariant variant = true;
+  ASSERT_EQ(String("1"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, LongAsBool) {
+  JsonVariant variant = 42L;
+  ASSERT_TRUE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, LongZeroAsBool) {
+  JsonVariant variant = 0L;
+  ASSERT_FALSE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, LongAsDouble) {
+  JsonVariant variant = 42L;
+  ASSERT_EQ(42.0, variant.as<double>());
+}
+
+TEST(JsonVariant_As_Tests, LongAsString) {
+  JsonVariant variant = 42L;
+  ASSERT_EQ(String("42"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, LongZeroAsDouble) {
+  JsonVariant variant = 0L;
+  ASSERT_EQ(0.0, variant.as<double>());
+}
+
+TEST(JsonVariant_As_Tests, NullAsBool) {
+  JsonVariant variant = null;
+  ASSERT_FALSE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, NullAsDouble) {
+  JsonVariant variant = null;
+  ASSERT_EQ(0.0, variant.as<double>());
+}
+
+TEST(JsonVariant_As_Tests, NullAsLong) {
+  JsonVariant variant = null;
+  ASSERT_EQ(0L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, NullAsString) {
+  JsonVariant variant = null;
+  ASSERT_EQ(String("null"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, NumberStringAsBool) {
+  JsonVariant variant = "42";
+  ASSERT_TRUE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, NumberStringAsLong) {
+  JsonVariant variant = "42";
+  ASSERT_EQ(42L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, RandomStringAsBool) {
+  JsonVariant variant = "hello";
+  ASSERT_FALSE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, RandomStringAsLong) {
+  JsonVariant variant = "hello";
+  ASSERT_EQ(0L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, TrueStringAsBool) {
+  JsonVariant variant = "true";
+  ASSERT_TRUE(variant.as<bool>());
+}
+
+TEST(JsonVariant_As_Tests, TrueStringAsLong) {
+  JsonVariant variant = "true";
+  ASSERT_EQ(1L, variant.as<long>());
+}
+
+TEST(JsonVariant_As_Tests, ObjectAsString) {
+  DynamicJsonBuffer buffer;
+
+  JsonObject& obj = buffer.createObject();
+  obj["key"] = "value";
+
+  JsonVariant variant = obj;
+  ASSERT_EQ(String("{\"key\":\"value\"}"), variant.as<String>());
+}
+
+TEST(JsonVariant_As_Tests, ArrayAsString) {
+  DynamicJsonBuffer buffer;
+
+  JsonArray& arr = buffer.createArray();
+  arr.add(4);
+  arr.add(2);
+
+  JsonVariant variant = arr;
+  ASSERT_EQ(String("[4,2]"), variant.as<String>());
+}

+ 92 - 0
test/JsonVariant_Is_Tests.cpp

@@ -0,0 +1,92 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#include <gtest/gtest.h>
+#define ARDUINOJSON_ENABLE_STD_STREAM
+#include <ArduinoJson.h>
+
+#define SUITE JsonVariant_Is_Tests
+
+using namespace ArduinoJson::Internals;
+
+template <typename TTo, typename TFrom>
+void assertIsNot(TFrom value) {
+  JsonVariant variant = value;
+  ASSERT_FALSE(variant.is<TTo>());
+}
+
+template <typename TTo>
+void assertIsNot(JsonArray& value) {
+  JsonVariant variant = value;
+  ASSERT_FALSE(variant.is<TTo>());
+}
+
+template <typename TTo, typename TFrom>
+void assertIs(TFrom value) {
+  JsonVariant variant = value;
+  ASSERT_TRUE(variant.is<TTo>());
+}
+
+template <typename TTo>
+void assertIs(JsonArray& value) {
+  JsonVariant variant = value;
+  ASSERT_TRUE(variant.is<TTo>());
+}
+
+TEST(SUITE, ArrayIsArry) { assertIs<JsonArray&>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsBool) { assertIsNot<bool>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsDouble) { assertIsNot<double>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsFloat) { assertIsNot<float>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsInt) { assertIsNot<int>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsLong) { assertIsNot<long>(JsonArray::invalid()); }
+TEST(SUITE, ArrayIsString) { assertIsNot<const char*>(JsonArray::invalid()); }
+
+TEST(SUITE, BoolIsArray) { assertIsNot<JsonArray&>(true); }
+TEST(SUITE, BoolIsBool) { assertIs<bool>(true); }
+TEST(SUITE, BoolIsDouble) { assertIsNot<double>(true); }
+TEST(SUITE, BoolIsFloat) { assertIsNot<float>(true); }
+TEST(SUITE, BoolIsInt) { assertIsNot<int>(true); }
+TEST(SUITE, BoolIsLong) { assertIsNot<long>(true); }
+TEST(SUITE, BoolIsString) { assertIsNot<const char*>(true); }
+
+TEST(SUITE, DoubleIsArray) { assertIsNot<JsonArray&>(4.2); }
+TEST(SUITE, DoubleIsBool) { assertIsNot<bool>(4.2); }
+TEST(SUITE, DoubleIsDouble) { assertIs<double>(4.2); }
+TEST(SUITE, DoubleIsFloat) { assertIs<float>(4.2); }
+TEST(SUITE, DoubleIsInt) { assertIsNot<int>(4.2); }
+TEST(SUITE, DoubleIsLong) { assertIsNot<long>(4.2); }
+TEST(SUITE, DoubleIsString) { assertIsNot<const char*>(4.2); }
+
+TEST(SUITE, LongIsArray) { assertIsNot<JsonArray&>(42L); }
+TEST(SUITE, LongIsBool) { assertIsNot<bool>(42L); }
+TEST(SUITE, LongIsDouble) { assertIsNot<double>(42L); }
+TEST(SUITE, LongIsFloat) { assertIsNot<float>(42L); }
+TEST(SUITE, LongIsInt) { assertIs<int>(42L); }
+TEST(SUITE, LongIsLong) { assertIs<long>(42L); }
+TEST(SUITE, LongIsString) { assertIsNot<const char*>(42L); }
+
+TEST(SUITE, StringIsArray) { assertIsNot<JsonArray&>("42"); }
+TEST(SUITE, StringIsBool) { assertIsNot<bool>("42"); }
+TEST(SUITE, StringIsDouble) { assertIsNot<double>("42"); }
+TEST(SUITE, StringIsFloat) { assertIsNot<float>("42"); }
+TEST(SUITE, StringIsInt) { assertIsNot<int>("42"); }
+TEST(SUITE, StringIsLong) { assertIsNot<long>("42"); }
+TEST(SUITE, StringIsString) { assertIs<const char*>("42"); }
+
+TEST(SUITE, UnparsedIntIsArra) { assertIsNot<JsonArray&>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsBool) { assertIsNot<bool>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsDouble) { assertIsNot<double>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsFloat) { assertIsNot<float>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsInt) { assertIs<int>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsLong) { assertIs<long>(Unparsed("42")); }
+TEST(SUITE, UnparsedIntIsString) { assertIsNot<const char*>(Unparsed("42")); }
+
+TEST(SUITE, UnparsedFloatIsBool) { assertIsNot<bool>(Unparsed("4.2e-10")); }
+TEST(SUITE, UnparsedFloatIsDouble) { assertIs<double>(Unparsed("4.2e-10")); }
+TEST(SUITE, UnparsedFloatIsFloat) { assertIs<float>(Unparsed("4.2e-10")); }
+TEST(SUITE, UnparsedFloatIsInt) { assertIsNot<int>(Unparsed("4.2e-10")); }
+TEST(SUITE, UnparsedFloatIsLong) { assertIsNot<long>(Unparsed("4.2e-10")); }
+TEST(SUITE, UnparsedFloatIsStr) { assertIsNot<const char*>(Unparsed("4.2")); }

+ 2 - 2
test/JsonWriter_WriteString_Tests.cpp

@@ -7,14 +7,14 @@
 #include <gtest/gtest.h>
 
 #include <ArduinoJson/Internals/JsonWriter.hpp>
-#include <ArduinoJson/Internals/StringBuilder.hpp>
+#include <ArduinoJson/Internals/StaticStringBuilder.hpp>
 
 using namespace ArduinoJson::Internals;
 
 class JsonWriter_WriteString_Tests : public testing::Test {
  protected:
   void whenInputIs(const char *input) {
-    StringBuilder sb(buffer, sizeof(buffer));
+    StaticStringBuilder sb(buffer, sizeof(buffer));
     JsonWriter writer(sb);
     writer.writeString(input);
     returnValue = writer.bytesWritten();

+ 2 - 2
test/StringBuilderTests.cpp

@@ -5,14 +5,14 @@
 // https://github.com/bblanchon/ArduinoJson
 
 #include <gtest/gtest.h>
-#include <ArduinoJson/Internals/StringBuilder.hpp>
+#include <ArduinoJson/Internals/StaticStringBuilder.hpp>
 
 using namespace ArduinoJson::Internals;
 
 class StringBuilderTests : public testing::Test {
  protected:
   virtual void SetUp() {
-    _stringBuilder = new StringBuilder(_buffer, sizeof(_buffer));
+    _stringBuilder = new StaticStringBuilder(_buffer, sizeof(_buffer));
   }
 
   virtual void TearDown() { delete _stringBuilder; }