1 // -*- mode: c++; c-basic-offset: 4; -*-
2 
3 // Author: Hong Jiang <hong@hjiang.net>
4 // Contributors:
5 //   Sean Middleditch <sean@middleditch.us>
6 //   rlyeh <https://github.com/r-lyeh>
7 
8 #pragma once
9 
10 #include <cstddef>
11 #include <cassert>
12 #include <iostream>
13 #include <map>
14 #include <vector>
15 #include <string>
16 #include <sstream>
17 
18 // jsonxx versioning: major.minor-extra where
19 // major = { number }
20 // minor = { number }
21 // extra = { 'a':alpha, 'b':beta, 'rc': release candidate, 'r': release, 's':stable }
22 #define JSONXX_MAJOR    "0"
23 #define JSONXX_MINOR    "22"
24 #define JSONXX_EXTRA    "a"
25 #define JSONXX_VERSION  JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA
26 #define JSONXX_XML_TAG  "<!-- generated by jsonxx " JSONXX_VERSION " -->"
27 
28 #if __cplusplus > 199711L
29 #define JSONXX_COMPILER_HAS_CXX11 1
30 #elif defined(_MSC_VER) && _MSC_VER > 1700
31 #define JSONXX_COMPILER_HAS_CXX11 1
32 #else
33 #define JSONXX_COMPILER_HAS_CXX11 0
34 #endif
35 
36 #ifdef _MSC_VER
37 // disable the C4127 warning if using VC, see http://stackoverflow.com/a/12042515
38 #define JSONXX_ASSERT(...) \
39   do { \
40     __pragma(warning(push)) __pragma(warning(disable:4127)) \
41     if( jsonxx::Assertions ) \
42     __pragma(warning(pop)) \
43       jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); \
44   __pragma(warning(push)) __pragma(warning(disable:4127)) \
45   } while(0) \
46   __pragma(warning(pop))
47 #else
48 #define JSONXX_ASSERT(...) do { if( jsonxx::Assertions ) \
49   jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); } while(0)
50 #endif
51 
52 namespace jsonxx {
53 
54 // FIXME(hjiang): Those should really be dynamic.
55 // Settings
56 enum Settings {
57   // constants
58   Enabled = true,
59   Disabled = false,
60   Permissive = true,
61   Strict = false,
62   // values
63   Parser = Permissive,  // permissive or strict parsing
64   UnquotedKeys = Disabled, // support of unquoted keys
65   Assertions = Enabled  // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds)
66 };
67 
68 #ifdef _MSC_VER
69 #pragma warning(push)
70 #pragma warning(disable:4127)
71 #endif
parser_is_strict()72 inline bool parser_is_strict() { return Parser == Strict; }
parser_is_permissive()73 inline bool parser_is_permissive() { return Parser == Permissive; }
unquoted_keys_are_enabled()74 inline bool unquoted_keys_are_enabled() { return UnquotedKeys == Enabled; }
75 #ifdef _MSC_VER
76 #pragma warning(pop)
77 #endif
78 
79 // Constants for .write() and .xml() methods
80 enum Format {
81   JSON      = 0,     // JSON output
82   JSONx     = 1,     // XML output, JSONx  format. see http://goo.gl/I3cxs
83   JXML      = 2,     // XML output, JXML   format. see https://github.com/r-lyeh/JXML
84   JXMLex    = 3,     // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex
85   TaggedXML = 4      // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12
86 };
87 
88 // Types
89 typedef long double Number;
90 typedef bool Boolean;
91 typedef std::string String;
92 struct Null {};
93 class Value;
94 class Object;
95 class Array;
96 
97 // Identity meta-function
98 template <typename T>
99 struct identity {
100   typedef T type;
101 };
102 
103 // Tools
104 bool validate( const std::string &input );
105 bool validate( std::istream &input );
106 std::string reformat( const std::string &input );
107 std::string reformat( std::istream &input );
108 std::string xml( const std::string &input, unsigned format = JSONx );
109 std::string xml( std::istream &input, unsigned format = JSONx );
110 
111 // Detail
112 void assertion( const char *file, int line, const char *expression, bool result );
113 
114 // A JSON Object
115 class Object {
116  public:
117   Object();
118   ~Object();
119 
120   template <typename T>
121   bool has(const std::string& key) const;
122 
123   // Always call has<>() first. If the key doesn't exist, consider
124   // the behavior undefined.
125   template <typename T>
126   T& get(const std::string& key);
127   template <typename T>
128   const T& get(const std::string& key) const;
129 
130   template <typename T>
131   const T& get(const std::string& key, const typename identity<T>::type& default_value) const;
132 
133   size_t size() const;
134   bool empty() const;
135 
136   const std::map<std::string, Value*>& kv_map() const;
137   std::string json() const;
138   std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const;
139   std::string write( unsigned format ) const;
140 
141   void reset();
142   bool parse(std::istream &input);
143   bool parse(const std::string &input);
144   typedef std::map<std::string, Value*> container;
145   void import( const Object &other );
146   void import( const std::string &key, const Value &value );
147   Object &operator<<(const Value &value);
148   Object &operator<<(const Object &value);
149   Object &operator=(const Object &value);
150   Object(const Object &other);
151   Object(const std::string &key, const Value &value);
152   template<size_t N>
Object(const char (& key)[N],const Value & value)153   Object(const char (&key)[N], const Value &value) {
154     import(key,value);
155   }
156   template<typename T>
157   Object &operator<<(const T &value);
158 
159  protected:
160   static bool parse(std::istream& input, Object& object);
161   container value_map_;
162   std::string odd;
163 };
164 
165 class Array {
166  public:
167   Array();
168   ~Array();
169 
170   size_t size() const;
171   bool empty() const;
172 
173   template <typename T>
174   bool has(unsigned int i) const;
175 
176   template <typename T>
177   T& get(unsigned int i);
178   template <typename T>
179   const T& get(unsigned int i) const;
180 
181   template <typename T>
182   const T& get(unsigned int i, const typename identity<T>::type& default_value) const;
183 
values()184   const std::vector<Value*>& values() const {
185     return values_;
186   }
187   std::string json() const;
188   std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const;
189 
write(unsigned format)190   std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); }
191   void reset();
192   bool parse(std::istream &input);
193   bool parse(const std::string &input);
194   typedef std::vector<Value*> container;
195   void append(const Array &other);
append(const Value & value)196   void append(const Value &value) { import(value); }
197   void import(const Array &other);
198   void import(const Value &value);
199   Array &operator<<(const Array &other);
200   Array &operator<<(const Value &value);
201   Array &operator=(const Array &other);
202   Array &operator=(const Value &value);
203   Array(const Array &other);
204   Array(const Value &value);
205  protected:
206   static bool parse(std::istream& input, Array& array);
207   container values_;
208 };
209 
210 // A value could be a number, an array, a string, an object, a
211 // boolean, or null
212 class Value {
213  public:
214 
215   Value();
~Value()216   ~Value() { reset(); }
217   void reset();
218 
219   template<typename T>
import(const T &)220   void import( const T & ) {
221     reset();
222     type_ = INVALID_;
223     // debug
224     // std::cout << "[WARN] No support for " << typeid(t).name() << std::endl;
225   }
import(const bool & b)226   void import( const bool &b ) {
227     reset();
228     type_ = BOOL_;
229     bool_value_ = b;
230   }
231 #define $number(TYPE) \
232   void import( const TYPE &n ) { \
233     reset(); \
234     type_ = NUMBER_; \
235     number_value_ = static_cast<long double>(n); \
236   }
237   $number( char )
$number(int)238   $number( int )
239   $number( long )
240   $number( long long )
241   $number( unsigned char )
242   $number( unsigned int )
243   $number( unsigned long )
244   $number( unsigned long long )
245   $number( float )
246   $number( double )
247   $number( long double )
248 #undef $number
249 #if JSONXX_COMPILER_HAS_CXX11 > 0
250   void import( const std::nullptr_t & ) {
251     reset();
252     type_ = NULL_;
253   }
254 #endif
import(const Null &)255   void import( const Null & ) {
256     reset();
257     type_ = NULL_;
258   }
import(const String & s)259   void import( const String &s ) {
260     reset();
261     type_ = STRING_;
262     *( string_value_ = new String() ) = s;
263   }
import(const Array & a)264   void import( const Array &a ) {
265     reset();
266     type_ = ARRAY_;
267     *( array_value_ = new Array() ) = a;
268   }
import(const Object & o)269   void import( const Object &o ) {
270     reset();
271     type_ = OBJECT_;
272     *( object_value_ = new Object() ) = o;
273   }
import(const Value & other)274   void import( const Value &other ) {
275     if (this != &other)
276     switch (other.type_) {
277       case NULL_:
278         import( Null() );
279         break;
280       case BOOL_:
281         import( other.bool_value_ );
282         break;
283       case NUMBER_:
284         import( other.number_value_ );
285         break;
286       case STRING_:
287         import( *other.string_value_ );
288         break;
289       case ARRAY_:
290         import( *other.array_value_ );
291         break;
292       case OBJECT_:
293         import( *other.object_value_ );
294         break;
295       case INVALID_:
296         type_ = INVALID_;
297         break;
298       default:
299         JSONXX_ASSERT( !"not implemented" );
300     }
301   }
302   template<typename T>
303   Value &operator <<( const T &t ) {
304     import(t);
305     return *this;
306   }
307   template<typename T>
308   Value &operator =( const T &t ) {
309     reset();
310     import(t);
311     return *this;
312   }
313   Value(const Value &other);
314   template<typename T>
Value(const T & t)315   Value( const T&t ) : type_(INVALID_) { import(t); }
316   template<size_t N>
Value(const char (& t)[N])317   Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); }
318 
319   bool parse(std::istream &input);
320   bool parse(const std::string &input);
321 
322   template<typename T>
323   bool is() const;
324   template<typename T>
325   T& get();
326   template<typename T>
327   const T& get() const;
328 
329   bool empty() const;
330 
331  public:
332   enum {
333     NUMBER_,
334     STRING_,
335     BOOL_,
336     NULL_,
337     ARRAY_,
338     OBJECT_,
339     INVALID_
340   } type_;
341   union {
342     Number number_value_;
343     String* string_value_;
344     Boolean bool_value_;
345     Array* array_value_;
346     Object* object_value_;
347   };
348 
349 protected:
350   static bool parse(std::istream& input, Value& value);
351 };
352 
353 template <typename T>
has(unsigned int i)354 bool Array::has(unsigned int i) const {
355   if (i >= size()) {
356     return false;
357   } else {
358     Value* v = values_.at(i);
359     return v->is<T>();
360   }
361 }
362 
363 template <typename T>
get(unsigned int i)364 T& Array::get(unsigned int i) {
365   JSONXX_ASSERT(i < size());
366   Value* v = values_.at(i);
367   return v->get<T>();
368 }
369 
370 template <typename T>
get(unsigned int i)371 const T& Array::get(unsigned int i) const {
372   JSONXX_ASSERT(i < size());
373   const Value* v = values_.at(i);
374   return v->get<T>();
375 }
376 
377 template <typename T>
get(unsigned int i,const typename identity<T>::type & default_value)378 const T& Array::get(unsigned int i, const typename identity<T>::type& default_value) const {
379   if(has<T>(i)) {
380     const Value* v = values_.at(i);
381     return v->get<T>();
382   } else {
383     return default_value;
384   }
385 }
386 
387 template <typename T>
has(const std::string & key)388 bool Object::has(const std::string& key) const {
389   container::const_iterator it(value_map_.find(key));
390   return it != value_map_.end() && it->second->is<T>();
391 }
392 
393 template <typename T>
get(const std::string & key)394 T& Object::get(const std::string& key) {
395   JSONXX_ASSERT(has<T>(key));
396   return value_map_.find(key)->second->get<T>();
397 }
398 
399 template <typename T>
get(const std::string & key)400 const T& Object::get(const std::string& key) const {
401   JSONXX_ASSERT(has<T>(key));
402   return value_map_.find(key)->second->get<T>();
403 }
404 
405 template <typename T>
get(const std::string & key,const typename identity<T>::type & default_value)406 const T& Object::get(const std::string& key, const typename identity<T>::type& default_value) const {
407   if (has<T>(key)) {
408     return value_map_.find(key)->second->get<T>();
409   } else {
410     return default_value;
411   }
412 }
413 
414 template<>
415 inline bool Value::is<Value>() const {
416     return true;
417 }
418 
419 template<>
420 inline bool Value::is<Null>() const {
421   return type_ == NULL_;
422 }
423 
424 template<>
425 inline bool Value::is<Boolean>() const {
426   return type_ == BOOL_;
427 }
428 
429 template<>
430 inline bool Value::is<String>() const {
431   return type_ == STRING_;
432 }
433 
434 template<>
435 inline bool Value::is<Number>() const {
436   return type_ == NUMBER_;
437 }
438 
439 template<>
440 inline bool Value::is<Array>() const {
441   return type_ == ARRAY_;
442 }
443 
444 template<>
445 inline bool Value::is<Object>() const {
446   return type_ == OBJECT_;
447 }
448 
449 template<>
450 inline Value& Value::get<Value>() {
451     return *this;
452 }
453 
454 template<>
455 inline const Value& Value::get<Value>() const {
456     return *this;
457 }
458 
459 template<>
460 inline bool& Value::get<Boolean>() {
461   JSONXX_ASSERT(is<Boolean>());
462   return bool_value_;
463 }
464 
465 template<>
466 inline std::string& Value::get<String>() {
467   JSONXX_ASSERT(is<String>());
468   return *string_value_;
469 }
470 
471 template<>
472 inline Number& Value::get<Number>() {
473   JSONXX_ASSERT(is<Number>());
474   return number_value_;
475 }
476 
477 template<>
478 inline Array& Value::get<Array>() {
479   JSONXX_ASSERT(is<Array>());
480   return *array_value_;
481 }
482 
483 template<>
484 inline Object& Value::get<Object>() {
485   JSONXX_ASSERT(is<Object>());
486   return *object_value_;
487 }
488 
489 template<>
490 inline const Boolean& Value::get<Boolean>() const {
491   JSONXX_ASSERT(is<Boolean>());
492   return bool_value_;
493 }
494 
495 template<>
496 inline const String& Value::get<String>() const {
497   JSONXX_ASSERT(is<String>());
498   return *string_value_;
499 }
500 
501 template<>
502 inline const Number& Value::get<Number>() const {
503   JSONXX_ASSERT(is<Number>());
504   return number_value_;
505 }
506 
507 template<>
508 inline const Array& Value::get<Array>() const {
509   JSONXX_ASSERT(is<Array>());
510   return *array_value_;
511 }
512 
513 template<>
514 inline const Object& Value::get<Object>() const {
515   JSONXX_ASSERT(is<Object>());
516   return *object_value_;
517 }
518 
519 template<typename T>
520 inline Object &Object::operator<<(const T &value) {
521   return *this << Value(value), *this;
522 }
523 
524 }  // namespace jsonxx
525 
526 std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v);
527 std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v);
528 std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v);
529