Adobe Source Libraries  1.43
json.hpp
Go to the documentation of this file.
1 /*
2  Copyright 2013 Adobe
3  Distributed under the Boost Software License, Version 1.0.
4  (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 */
6 /**************************************************************************************************/
7 
8 #ifndef ADOBE_JSON_HPP
9 #define ADOBE_JSON_HPP
10 
11 #include <cfloat>
12 #include <cmath>
13 #include <cstddef>
14 #include <cstdint>
15 #include <stdexcept>
16 #include <string>
17 #include <string>
18 #include <unordered_map>
19 #include <vector>
20 
21 #include <double-conversion/src/double-conversion.h>
22 
23 #include <adobe/cassert.hpp>
24 #include <adobe/string/to_string.hpp>
25 
26 /**************************************************************************************************/
30 namespace adobe {
31 
32 /**************************************************************************************************/
52 template <typename T>
53 class json_parser {
54  constexpr static double kNaN = std::numeric_limits<double>::quiet_NaN();
55  public:
56  typedef typename T::object_type object_type;
57  typedef typename T::array_type array_type;
58  typedef typename T::value_type value_type;
59  typedef typename T::string_type string_type;
60  typedef typename T::key_type key_type;
61 
62  /*
63  We want both empty and error values to be infinity as it will be an invalid
64  JSON value and is something we can check against to assert validity. We also
65  allow 'junk' as it is the end of this token and on to the next one.
66  */
67  explicit json_parser(const char* p) :
68  p_(p),
69  s2d_(double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
70  kNaN, kNaN, nullptr, nullptr)
71  { }
72 
74  value_type result;
75  require(is_object(result) || is_array(result), "object or array");
76  return result;
77  }
78 
79  private:
80  bool is_object(value_type& t) {
81  if (!is_structural_char('{')) return false;
84  if (is_string(string)) {
85  require(is_structural_char(':'), ":");
86  value_type value;
87  require(is_value(value), "value");
88  T::move_append(object, string, value);
89 
90  while (is_structural_char(',')) {
91  require(is_string(string), "string");
92  require(is_structural_char(':'), ":");
93  require(is_value(value), "value");
94  T::move_append(object, string, value);
95  }
96  }
97  require(is_structural_char('}'), "}");
98  t = move(object);
99  return true;
100  }
101 
102  bool is_array(value_type& t) {
103  if (!is_structural_char('[')) return false;
105  value_type value;
106  if (is_value(value)) {
107  T::move_append(array, value);
108  while (is_structural_char(',')) {
109  require(is_value(value), "value");
110  T::move_append(array, value);
111  }
112  }
113  require(is_structural_char(']'), "]");
114  t = move(array);
115  return true;
116  }
117 
118  // REVISIT (sparent) : should use peek to avoid steping in/out of multiple functions.
119 
120  bool is_value(value_type& t) {
121  return is_string(t)
122  || is_number(t)
123  || is_object(t)
124  || is_array(t)
125  || is_bool(t)
126  || is_null(t);
127  }
128 
129  // requires at least one character
130 
131  bool is_sequence(const char* p) {
132  if (!is_char(*p)) return false;
133  ++p;
134  while (is_char(*p)) ++p;
135  require(*p == 0, "valid constant");
136  return true;
137  }
138 
139  bool is_bool(value_type& t) {
140  if (is_sequence("true")) { t = true; return true; }
141  if (is_sequence("false")) { t = false; return true; }
142  return false;
143  }
144 
145  bool is_null(value_type& t) {
146  if (!is_sequence("null")) return false;
147  t = value_type();
148  return true;
149  }
150 
151  template <typename S> // S is string_type or key_type
152  bool is_string(S& s) {
153  if (!is_char('"')) return false;
154  while (!is_char('"')) {
155  const char* f = p_;
156  while (!('\x00' <= *p_ && *p_ < '\x20') && *p_ != '"' && *p_ != '\\') ++p_;
157  require(!('\x00' <= *p_ && *p_ < '\x20'), "valid character");
158  T::append(s, f, p_);
159  if (!is_char('\\')) continue;
160  char c = escape()[static_cast<unsigned char>(*p_++)];
161  require(c, "valid escape character");
162  if (c == 'u') {
163  std::uint32_t utf = get_four_digits();
164  // handle surrogate leading
165  if (0xD800 <= utf && utf <= 0xDBFF) {
166  require(is_char('u'), "trail surrogate");
167  std::uint32_t trailing = get_four_digits();
168  require(0xDC00 <= trailing && trailing <= 0xDFFF, "trail surrogate");
169  utf = ((utf - 0xD800) << 10) + (trailing - 0xDC00);
170  }
171  char buffer[4];
172  auto end = utf8(utf, buffer);
173  T::append(s, buffer, end);
174  }
175  else T::append(s, &c, &c + 1);
176  }
177  return true;
178  }
179 
180  bool is_string(value_type& t) {
182  bool result = is_string(string);
183  if (result) t = move(string);
184  return result;
185  }
186 
187  bool is_number(value_type& t) {
188  const char* p = p_;
189  bool neg = is_char('-');
190  if (!is_int()) {
191  require(!neg, "digit");
192  return false;
193  }
194  frac();
195  exp();
196 
197  int count = 0;
198  double value = s2d_.StringToDouble(p, static_cast<int>(p_ - p), &count);
199 
200  require(std::isfinite(value), "finite number");
201  ADOBE_ASSERT(count == p_ - p && "StringToDouble() failure");
202 
203  t = value;
204  return true;
205  }
206 
207  void frac() {
208  if (!is_char('.')) return;
209  require(is_digit(), "digit");
210 
211  while (is_digit()) ;
212  }
213 
214  void exp() {
215  if (!is_char('e') && !is_char('E')) return;
216  is_char('-') || is_char('+');
217  require(is_digit(), "exponent digit");
218  while (is_digit()) ;
219  }
220 
221  bool is_digit() {
222  if (!('0' <= *p_ && *p_ <= '9')) return false;
223  ++p_;
224  return true;
225  }
226 
227  bool is_int() {
228  if (is_char('0')) return true;
229  if (!is_digit()) return false;
230  while (is_digit()) ;
231  return true;
232  }
233 
234  std::uint32_t get_four_digits() {
235  std::uint32_t result;
236  char digit = hex_digit()[static_cast<unsigned char>(*p_++)];
237  if (digit == '\xFF') goto fail;
238  result = digit * 0x1000;
239  digit = hex_digit()[static_cast<unsigned char>(*p_++)];
240  if (digit == '\xFF') goto fail;
241  result += digit * 0x0100;
242  digit = hex_digit()[static_cast<unsigned char>(*p_++)];
243  if (digit == '\xFF') goto fail;
244  result += digit * 0x0010;
245  digit = hex_digit()[static_cast<unsigned char>(*p_++)];
246  if (digit == '\xFF') goto fail;
247  result += digit;
248  return result;
249  fail:
250  require(false, "four hex digits");
251  return 0;
252  }
253 
258  void require(bool x, const char* failure) {
259  using namespace std;
260 
261  if (!x) throw logic_error(failure + string(" is required"));
262  }
263 
265  void skip_white_space() { while (ws()[static_cast<unsigned char>(*p_)]) { ++p_; } }
266 
267  bool is_char(char x) {
268  if (*p_ != x) return false;
269  ++p_;
270  return true;
271  }
272 
273  bool is_structural_char(char x) {
274  skip_white_space();
275  if (!is_char(x)) return false;
276  skip_white_space();
277  return true;
278  }
279 
281  char* utf8(std::uint32_t x, char* f) {
282  // Should assert non-surrogate
283  std::size_t bytes_to_write(0);
284 
285  if (x < 0x80) bytes_to_write = 1;
286  else if (x < 0x800) bytes_to_write = 2;
287  else if (x < 0x10000) bytes_to_write = 3;
288  else if (x < 0x0010FFFF) bytes_to_write = 4;
289  else require(false, "valid utf-32 character");
290 
291  const std::uint32_t mark = 0x80;
292  const std::uint32_t mask = 0xBF;
293 
294  static const unsigned char first_mark[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
295 
296  f += bytes_to_write;
297 
298  switch (bytes_to_write) { /* note: everything falls through. */
299  case 4: *--f = static_cast<char>((x | mark) & mask); x >>= 6;
300  case 3: *--f = static_cast<char>((x | mark) & mask); x >>= 6;
301  case 2: *--f = static_cast<char>((x | mark) & mask); x >>= 6;
302  case 1: *--f = static_cast<char> (x | first_mark[bytes_to_write]);
303  }
304 
305  return f + bytes_to_write;
306  }
307 
308  const char* p_;
309  double_conversion::StringToDoubleConverter s2d_;
310 
311  typedef char table_t_[256];
312 
313  static const table_t_& hex_digit() {
314  static const char hex_digit_[] = {
315  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
316  /* 0 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
317  /* 1 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
318  /* 2 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
319  /* 3 */ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
320  /* 4 */ '\xFF', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
321  /* 5 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
322  /* 6 */ '\xFF', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
323  /* 7 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
324  /* 8 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
325  /* 9 */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
326  /* A */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
327  /* B */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
328  /* C */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
329  /* D */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
330  /* E */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF',
331  /* F */ '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF'
332  };
333  return hex_digit_;
334  }
335 
336  static const table_t_& escape() {
337  static const char escape_[] = {
338  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
339  /* 0 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
340  /* 1 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
341  /* 2 */ '\x00', '\x00', '\x22', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x2F',
342  /* 3 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
343  /* 4 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
344  /* 5 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x5C', '\x00', '\x00', '\x00',
345  /* 6 */ '\x00', '\x00', '\x08', '\x00', '\x00', '\x00', '\x0C', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0A', '\x00',
346  /* 7 */ '\x00', '\x00', '\x0D', '\x00', '\x09', '\x75', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
347  /* 8 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
348  /* 9 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
349  /* A */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
350  /* B */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
351  /* C */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
352  /* D */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
353  /* E */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
354  /* F */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
355  };
356  return escape_;
357  }
358 
359  static const table_t_& ws() {
360  static const char ws_[] = {
361  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
362  /* 0 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x01', '\x00', '\x00', '\x01', '\x00', '\x00',
363  /* 1 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
364  /* 2 */ '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
365  /* 3 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
366  /* 4 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
367  /* 5 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
368  /* 6 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
369  /* 7 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
370  /* 8 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
371  /* 9 */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
372  /* A */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
373  /* B */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
374  /* C */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
375  /* D */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
376  /* E */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
377  /* F */ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'
378  };
379  return ws_;
380  }
381 };
382 
383 /**************************************************************************************************/
384 
385 enum class json_type {
387 };
388 
389 /**************************************************************************************************/
399 template <typename T,
400  typename O>
402  public:
403  typedef typename T::object_type object_type;
404  typedef typename T::pair_type pair_type; // type returned for *begin(object_type)
405  typedef typename T::array_type array_type;
406  typedef typename T::value_type value_type;
407  typedef typename T::string_type string_type;
408  typedef typename T::key_type key_type;
409 
410  json_generator(O out) : out_(out) { };
411 
412  O generate(const value_type& value, std::size_t indent = 0) {
413  switch (T::type(value)) {
414  case json_type::object:
415  case json_type::array:
416  generate_(value, indent);
417  break;
418  default:
419  require(false, "object or array");
420  }
421  return out_;
422  }
423 
424  private:
425  void require(bool x, const char* message) {
426  if (!x) throw std::logic_error(message);
427  }
428 
429  void generate_(const value_type& value, std::size_t indent) {
430  switch (T::type(value)) {
431  case json_type::object:
432  generate_(as<object_type>(value), indent);
433  break;
434  case json_type::array:
435  generate_(as<array_type>(value), indent);
436  break;
437  case json_type::string:
438  generate_string(as<string_type>(value));
439  break;
440  case json_type::number:
441  generate_(as<double>(value));
442  break;
443  case json_type::boolean:
444  generate_(as<bool>(value));
445  break;
446  case json_type::null:
447  generate_();
448  break;
449  default:
450  require(false, "valid type");
451  break;
452  }
453  }
454 
455  void generate_() {
456  static const char null_[] = "null";
457  out_ = std::copy(std::begin(null_), std::end(null_) - 1, out_);
458  }
459 
460  void generate_(bool x) {
461  static const char true_[] = "true";
462  static const char false_[] = "false";
463  if (x) out_ = std::copy(std::begin(true_), std::end(true_) - 1, out_);
464  else out_ = std::copy(std::begin(false_), std::end(false_) - 1, out_);
465  }
466 
467  void generate_(double x) {
468  require(!std::isnan(x) && !std::isinf(x), "valid double");
469  out_ = adobe::to_string(x, out_);
470  }
471 
472  template<typename U>
473  static const U& as(const value_type& x) { return T::template as<U>(x); }
474 
475  void indent(std::size_t n) {
476  for(std::size_t n_ = 0; n_ != n; ++n_) *out_++ = '\t';
477  }
478  void space() { *out_++ = ' ';}
479  void endl() { *out_++ = '\n'; }
480 
481  template <typename S>
482  void generate_string(const S& x) {
483  static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
484 
485  *out_++ = '"';
486  // REVISIT (sparent) : do escape handling here
487  for (const auto& e : x) {
488  if (('\x00' <= e && e <= '\x1F') || e == '"' || e == '\\') {
489  *out_++ = '\\';
490  switch (e) {
491  case '"': *out_++ = '"'; break;
492  case '\\': *out_++ = '\\'; break;
493  case '\b': *out_++ = 'b'; break;
494  case '\f': *out_++ = 'f'; break;
495  case '\n': *out_++ = 'n'; break;
496  case '\r': *out_++ = 'r'; break;
497  case '\t': *out_++ = 't'; break;
498  default:
499  *out_++ = 'u';
500  *out_++ = '0';
501  *out_++ = '0';
502  *out_++ = hex_digits[static_cast<unsigned char>((e >> 4) & '\x0F')];
503  *out_++ = hex_digits[static_cast<unsigned char>(e & '\x0F')];
504  break;
505  }
506  } else *out_++ = e;
507  }
508  *out_++ = '"';
509  }
510 
511  void generate_(const pair_type& value, std::size_t n) {
512  generate_string(value.first); *out_++ = ':'; space(); generate_(value.second, n);
513  }
514 
515  template <typename I> // I models forward iterator
516  bool list(I f, I l, std::size_t n) {
517  if (f == l) return false;
518  I next = f; ++next;
519 
520  bool is_one = next == l;
521 
522  if (is_one) { space(); }
523  else { endl(); n += 1; indent(n); }
524 
525  generate_(*f, n);
526 
527  if (is_one) { return false; }
528 
529  f = next;
530  while (f != l) {
531  *out_++ = ','; endl();
532  indent(n); generate_(*f, n);
533  ++f;
534  }
535  endl();
536  return true;
537  }
538 
539  void generate_(const object_type& value, std::size_t n) {
540  *out_++ = '{';
541  if (list(begin(value), end(value), n)) indent(n);
542  else space();
543  *out_++ = '}';
544  }
545 
546  void generate_(const array_type& value, std::size_t n) {
547  *out_++ = '[';
548  if (list(begin(value), end(value), n)) indent(n);
549  else space();
550  *out_++ = ']';
551  }
552 
553  O out_;
554 };
555 
556 /**************************************************************************************************/
557 
558 } // namespace adobe
559 
560 /**************************************************************************************************/
561 // ADOBE_JSON_HPP
562 #endif
563 
564 /**************************************************************************************************/
A utility class that uses a helper class to access a provided data structure and output well-formed J...
Definition: json.hpp:401
T::pair_type pair_type
Definition: json.hpp:404
T::string_type string_type
Definition: json.hpp:407
T::object_type object_type
Definition: json.hpp:403
A utility class that parses raw JSON data and uses a helper class to construct the desired representa...
Definition: json.hpp:53
T::key_type key_type
Definition: json.hpp:60
value_type parse()
Definition: json.hpp:73
T::string_type string_type
Definition: json.hpp:59
T::value_type value_type
Definition: json.hpp:58
json_parser(const char *p)
Definition: json.hpp:67
json_generator(O out)
Definition: json.hpp:410
json_type
Definition: json.hpp:385
T::array_type array_type
Definition: json.hpp:405
T::array_type array_type
Definition: json.hpp:57
T::key_type key_type
Definition: json.hpp:408
T::object_type object_type
Definition: json.hpp:56
T::value_type value_type
Definition: json.hpp:406
O generate(const value_type &value, std::size_t indent=0)
Definition: json.hpp:412