folly copyright 2015 -> copyright 2016
[folly.git] / folly / json.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/json.h>
18 #include <cassert>
19 #include <boost/next_prior.hpp>
20 #include <boost/algorithm/string.hpp>
21
22 #include <folly/Conv.h>
23 #include <folly/Portability.h>
24 #include <folly/Range.h>
25 #include <folly/String.h>
26 #include <folly/Unicode.h>
27
28 namespace folly {
29
30 //////////////////////////////////////////////////////////////////////
31
32 namespace json {
33 namespace {
34
35 char32_t decodeUtf8(
36     const unsigned char*& p,
37     const unsigned char* const e,
38     bool skipOnError) {
39   /* The following encodings are valid, except for the 5 and 6 byte
40    * combinations:
41    * 0xxxxxxx
42    * 110xxxxx 10xxxxxx
43    * 1110xxxx 10xxxxxx 10xxxxxx
44    * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
45    * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
46    * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
47    */
48
49   auto skip = [&] { ++p; return U'\ufffd'; };
50
51   if (p >= e) {
52     if (skipOnError) return skip();
53     throw std::runtime_error("folly::decodeUtf8 empty/invalid string");
54   }
55
56   unsigned char fst = *p;
57   if (!(fst & 0x80)) {
58     // trivial case
59     return *p++;
60   }
61
62   static const uint32_t bitMask[] = {
63     (1 << 7) - 1,
64     (1 << 11) - 1,
65     (1 << 16) - 1,
66     (1 << 21) - 1
67   };
68
69   // upper control bits are masked out later
70   uint32_t d = fst;
71
72   if ((fst & 0xC0) != 0xC0) {
73     if (skipOnError) return skip();
74     throw std::runtime_error(to<std::string>("folly::decodeUtf8 i=0 d=", d));
75   }
76
77   fst <<= 1;
78
79   for (unsigned int i = 1; i != 3 && p + i < e; ++i) {
80     unsigned char tmp = p[i];
81
82     if ((tmp & 0xC0) != 0x80) {
83       if (skipOnError) return skip();
84       throw std::runtime_error(
85         to<std::string>("folly::decodeUtf8 i=", i, " tmp=", (uint32_t)tmp));
86     }
87
88     d = (d << 6) | (tmp & 0x3F);
89     fst <<= 1;
90
91     if (!(fst & 0x80)) {
92       d &= bitMask[i];
93
94       // overlong, could have been encoded with i bytes
95       if ((d & ~bitMask[i - 1]) == 0) {
96         if (skipOnError) return skip();
97         throw std::runtime_error(
98           to<std::string>("folly::decodeUtf8 i=", i, " d=", d));
99       }
100
101       // check for surrogates only needed for 3 bytes
102       if (i == 2) {
103         if ((d >= 0xD800 && d <= 0xDFFF) || d > 0x10FFFF) {
104           if (skipOnError) return skip();
105           throw std::runtime_error(
106             to<std::string>("folly::decodeUtf8 i=", i, " d=", d));
107         }
108       }
109
110       p += i + 1;
111       return d;
112     }
113   }
114
115   if (skipOnError) return skip();
116   throw std::runtime_error("folly::decodeUtf8 encoding length maxed out");
117 }
118
119 struct Printer {
120   explicit Printer(fbstring& out,
121                    unsigned* indentLevel,
122                    serialization_opts const* opts)
123     : out_(out)
124     , indentLevel_(indentLevel)
125     , opts_(*opts)
126   {}
127
128   void operator()(dynamic const& v) const {
129     switch (v.type()) {
130     case dynamic::DOUBLE:
131       if (!opts_.allow_nan_inf &&
132           (std::isnan(v.asDouble()) || std::isinf(v.asDouble()))) {
133         throw std::runtime_error("folly::toJson: JSON object value was a "
134           "NaN or INF");
135       }
136       toAppend(v.asDouble(), &out_, opts_.double_mode, opts_.double_num_digits);
137       break;
138     case dynamic::INT64: {
139       auto intval = v.asInt();
140       if (opts_.javascript_safe) {
141         // Use folly::to to check that this integer can be represented
142         // as a double without loss of precision.
143         intval = int64_t(to<double>(intval));
144       }
145       toAppend(intval, &out_);
146       break;
147     }
148     case dynamic::BOOL:
149       out_ += v.asBool() ? "true" : "false";
150       break;
151     case dynamic::NULLT:
152       out_ += "null";
153       break;
154     case dynamic::STRING:
155       escapeString(v.asString(), out_, opts_);
156       break;
157     case dynamic::OBJECT:
158       printObject(v);
159       break;
160     case dynamic::ARRAY:
161       printArray(v);
162       break;
163     default:
164       CHECK(0) << "Bad type " << v.type();
165     }
166   }
167
168 private:
169   void printKV(const std::pair<const dynamic, dynamic>& p) const {
170     if (!opts_.allow_non_string_keys && !p.first.isString()) {
171       throw std::runtime_error("folly::toJson: JSON object key was not a "
172         "string");
173     }
174     (*this)(p.first);
175     mapColon();
176     (*this)(p.second);
177   }
178
179   template <typename Iterator>
180   void printKVPairs(Iterator begin, Iterator end) const {
181     printKV(*begin);
182     for (++begin; begin != end; ++begin) {
183       out_ += ',';
184       newline();
185       printKV(*begin);
186     }
187   }
188
189   void printObject(dynamic const& o) const {
190     if (o.empty()) {
191       out_ += "{}";
192       return;
193     }
194
195     out_ += '{';
196     indent();
197     newline();
198     if (opts_.sort_keys) {
199       std::vector<std::pair<dynamic, dynamic>> items(
200         o.items().begin(), o.items().end());
201       std::sort(items.begin(), items.end());
202       printKVPairs(items.begin(), items.end());
203     } else {
204       printKVPairs(o.items().begin(), o.items().end());
205     }
206     outdent();
207     newline();
208     out_ += '}';
209   }
210
211   void printArray(dynamic const& a) const {
212     if (a.empty()) {
213       out_ += "[]";
214       return;
215     }
216
217     out_ += '[';
218     indent();
219     newline();
220     (*this)(a[0]);
221     for (auto& val : range(boost::next(a.begin()), a.end())) {
222       out_ += ',';
223       newline();
224       (*this)(val);
225     }
226     outdent();
227     newline();
228     out_ += ']';
229   }
230
231 private:
232   void outdent() const {
233     if (indentLevel_) {
234       --*indentLevel_;
235     }
236   }
237
238   void indent() const {
239     if (indentLevel_) {
240       ++*indentLevel_;
241     }
242   }
243
244   void newline() const {
245     if (indentLevel_) {
246       out_ += to<fbstring>('\n', fbstring(*indentLevel_ * 2, ' '));
247     }
248   }
249
250   void mapColon() const {
251     out_ += indentLevel_ ? " : " : ":";
252   }
253
254 private:
255   fbstring& out_;
256   unsigned* const indentLevel_;
257   serialization_opts const& opts_;
258 };
259
260   //////////////////////////////////////////////////////////////////////
261
262   struct ParseError : std::runtime_error {
263     explicit ParseError(int line)
264       : std::runtime_error(to<std::string>("json parse error on line ", line))
265     {}
266
267     explicit ParseError(int line, std::string const& context,
268         std::string const& expected)
269       : std::runtime_error(to<std::string>("json parse error on line ", line,
270           !context.empty() ? to<std::string>(" near `", context, '\'')
271                           : "",
272           ": ", expected))
273     {}
274
275     explicit ParseError(std::string const& msg)
276       : std::runtime_error("json parse error: " + msg)
277     {}
278   };
279
280 // Wraps our input buffer with some helper functions.
281 struct Input {
282   explicit Input(StringPiece range, json::serialization_opts const* opts)
283       : range_(range)
284       , opts_(*opts)
285       , lineNum_(0)
286   {
287     storeCurrent();
288   }
289
290   Input(Input const&) = delete;
291   Input& operator=(Input const&) = delete;
292
293   char const* begin() const { return range_.begin(); }
294
295   // Parse ahead for as long as the supplied predicate is satisfied,
296   // returning a range of what was skipped.
297   template<class Predicate>
298   StringPiece skipWhile(const Predicate& p) {
299     std::size_t skipped = 0;
300     for (; skipped < range_.size(); ++skipped) {
301       if (!p(range_[skipped])) {
302         break;
303       }
304       if (range_[skipped] == '\n') {
305         ++lineNum_;
306       }
307     }
308     auto ret = range_.subpiece(0, skipped);
309     range_.advance(skipped);
310     storeCurrent();
311     return ret;
312   }
313
314   StringPiece skipDigits() {
315     return skipWhile([] (char c) { return c >= '0' && c <= '9'; });
316   }
317
318   StringPiece skipMinusAndDigits() {
319     bool firstChar = true;
320     return skipWhile([&firstChar] (char c) {
321         bool result = (c >= '0' && c <= '9') || (firstChar && c == '-');
322         firstChar = false;
323         return result;
324       });
325   }
326
327   void skipWhitespace() {
328     range_ = folly::skipWhitespace(range_);
329     storeCurrent();
330   }
331
332   void expect(char c) {
333     if (**this != c) {
334       throw ParseError(lineNum_, context(),
335         to<std::string>("expected '", c, '\''));
336     }
337     ++*this;
338   }
339
340   std::size_t size() const {
341     return range_.size();
342   }
343
344   int operator*() const {
345     return current_;
346   }
347
348   void operator++() {
349     range_.pop_front();
350     storeCurrent();
351   }
352
353   template<class T>
354   T extract() {
355     try {
356       return to<T>(&range_);
357     } catch (std::exception const& e) {
358       error(e.what());
359     }
360   }
361
362   bool consume(StringPiece str) {
363     if (boost::starts_with(range_, str)) {
364       range_.advance(str.size());
365       storeCurrent();
366       return true;
367     }
368     return false;
369   }
370
371   std::string context() const {
372     return range_.subpiece(0, 16 /* arbitrary */).toString();
373   }
374
375   dynamic error(char const* what) const {
376     throw ParseError(lineNum_, context(), what);
377   }
378
379   json::serialization_opts const& getOpts() {
380     return opts_;
381   }
382
383 private:
384   void storeCurrent() {
385     current_ = range_.empty() ? EOF : range_.front();
386   }
387
388 private:
389   StringPiece range_;
390   json::serialization_opts const& opts_;
391   unsigned lineNum_;
392   int current_;
393 };
394
395 dynamic parseValue(Input& in);
396 fbstring parseString(Input& in);
397 dynamic parseNumber(Input& in);
398
399 dynamic parseObject(Input& in) {
400   assert(*in == '{');
401   ++in;
402
403   dynamic ret = dynamic::object;
404
405   in.skipWhitespace();
406   if (*in == '}') {
407     ++in;
408     return ret;
409   }
410
411   for (;;) {
412     if (in.getOpts().allow_trailing_comma && *in == '}') {
413       break;
414     }
415     if (*in == '\"') { // string
416       auto key = parseString(in);
417       in.skipWhitespace();
418       in.expect(':');
419       in.skipWhitespace();
420       ret.insert(std::move(key), parseValue(in));
421     } else if (!in.getOpts().allow_non_string_keys) {
422       in.error("expected string for object key name");
423     } else {
424       auto key = parseValue(in);
425       in.skipWhitespace();
426       in.expect(':');
427       in.skipWhitespace();
428       ret.insert(std::move(key), parseValue(in));
429     }
430
431     in.skipWhitespace();
432     if (*in != ',') {
433       break;
434     }
435     ++in;
436     in.skipWhitespace();
437   }
438   in.expect('}');
439
440   return ret;
441 }
442
443 dynamic parseArray(Input& in) {
444   assert(*in == '[');
445   ++in;
446
447   dynamic ret = {};
448
449   in.skipWhitespace();
450   if (*in == ']') {
451     ++in;
452     return ret;
453   }
454
455   for (;;) {
456     if (in.getOpts().allow_trailing_comma && *in == ']') {
457       break;
458     }
459     ret.push_back(parseValue(in));
460     in.skipWhitespace();
461     if (*in != ',') {
462       break;
463     }
464     ++in;
465     in.skipWhitespace();
466   }
467   in.expect(']');
468
469   return ret;
470 }
471
472 dynamic parseNumber(Input& in) {
473   bool const negative = (*in == '-');
474   if (negative && in.consume("-Infinity")) {
475     if (in.getOpts().parse_numbers_as_strings) {
476       return "-Infinity";
477     } else {
478       return -std::numeric_limits<double>::infinity();
479     }
480   }
481
482   auto integral = in.skipMinusAndDigits();
483   if (negative && integral.size() < 2) {
484     in.error("expected digits after `-'");
485   }
486
487   auto const wasE = *in == 'e' || *in == 'E';
488
489   constexpr const char* maxInt = "9223372036854775807";
490   constexpr const char* minInt = "9223372036854775808";
491   constexpr auto maxIntLen = constexpr_strlen(maxInt);
492
493
494   if (*in != '.' && !wasE && in.getOpts().parse_numbers_as_strings) {
495     return integral;
496   }
497
498   if (*in != '.' && !wasE) {
499     if (LIKELY(!in.getOpts().double_fallback || integral.size() < maxIntLen) ||
500          (integral.size() == maxIntLen &&
501            (integral <= maxInt || (integral == minInt && negative)))) {
502       auto val = to<int64_t>(integral);
503       in.skipWhitespace();
504       return val;
505     } else {
506       auto val = to<double>(integral);
507       in.skipWhitespace();
508       return val;
509     }
510   }
511
512   auto end = !wasE ? (++in, in.skipDigits().end()) : in.begin();
513   if (*in == 'e' || *in == 'E') {
514     ++in;
515     if (*in == '+' || *in == '-') {
516       ++in;
517     }
518     auto expPart = in.skipDigits();
519     end = expPart.end();
520   }
521   auto fullNum = range(integral.begin(), end);
522   if (in.getOpts().parse_numbers_as_strings) {
523     return fullNum;
524   }
525   auto val = to<double>(fullNum);
526   return val;
527 }
528
529 fbstring decodeUnicodeEscape(Input& in) {
530   auto hexVal = [&] (char c) -> unsigned {
531     return c >= '0' && c <= '9' ? c - '0' :
532            c >= 'a' && c <= 'f' ? c - 'a' + 10 :
533            c >= 'A' && c <= 'F' ? c - 'A' + 10 :
534            (in.error("invalid hex digit"), 0);
535   };
536
537   auto readHex = [&]() -> uint16_t {
538     if (in.size() < 4) {
539       in.error("expected 4 hex digits");
540     }
541
542     uint16_t ret = hexVal(*in) * 4096;
543     ++in;
544     ret += hexVal(*in) * 256;
545     ++in;
546     ret += hexVal(*in) * 16;
547     ++in;
548     ret += hexVal(*in);
549     ++in;
550     return ret;
551   };
552
553   /*
554    * If the value encoded is in the surrogate pair range, we need to
555    * make sure there is another escape that we can use also.
556    */
557   uint32_t codePoint = readHex();
558   if (codePoint >= 0xd800 && codePoint <= 0xdbff) {
559     if (!in.consume("\\u")) {
560       in.error("expected another unicode escape for second half of "
561         "surrogate pair");
562     }
563     uint16_t second = readHex();
564     if (second >= 0xdc00 && second <= 0xdfff) {
565       codePoint = 0x10000 + ((codePoint & 0x3ff) << 10) +
566                   (second & 0x3ff);
567     } else {
568       in.error("second character in surrogate pair is invalid");
569     }
570   } else if (codePoint >= 0xdc00 && codePoint <= 0xdfff) {
571     in.error("invalid unicode code point (in range [0xdc00,0xdfff])");
572   }
573
574   return codePointToUtf8(codePoint);
575 }
576
577 fbstring parseString(Input& in) {
578   assert(*in == '\"');
579   ++in;
580
581   fbstring ret;
582   for (;;) {
583     auto range = in.skipWhile(
584       [] (char c) { return c != '\"' && c != '\\'; }
585     );
586     ret.append(range.begin(), range.end());
587
588     if (*in == '\"') {
589       ++in;
590       break;
591     }
592     if (*in == '\\') {
593       ++in;
594       switch (*in) {
595       case '\"':    ret.push_back('\"'); ++in; break;
596       case '\\':    ret.push_back('\\'); ++in; break;
597       case '/':     ret.push_back('/');  ++in; break;
598       case 'b':     ret.push_back('\b'); ++in; break;
599       case 'f':     ret.push_back('\f'); ++in; break;
600       case 'n':     ret.push_back('\n'); ++in; break;
601       case 'r':     ret.push_back('\r'); ++in; break;
602       case 't':     ret.push_back('\t'); ++in; break;
603       case 'u':     ++in; ret += decodeUnicodeEscape(in); break;
604       default:      in.error(to<fbstring>("unknown escape ", *in,
605                                           " in string").c_str());
606       }
607       continue;
608     }
609     if (*in == EOF) {
610       in.error("unterminated string");
611     }
612     if (!*in) {
613       /*
614        * Apparently we're actually supposed to ban all control
615        * characters from strings.  This seems unnecessarily
616        * restrictive, so we're only banning zero bytes.  (Since the
617        * string is presumed to be UTF-8 encoded it's fine to just
618        * check this way.)
619        */
620       in.error("null byte in string");
621     }
622
623     ret.push_back(*in);
624     ++in;
625   }
626
627   return ret;
628 }
629
630 dynamic parseValue(Input& in) {
631   in.skipWhitespace();
632   return *in == '[' ? parseArray(in) :
633          *in == '{' ? parseObject(in) :
634          *in == '\"' ? parseString(in) :
635          (*in == '-' || (*in >= '0' && *in <= '9')) ? parseNumber(in) :
636          in.consume("true") ? true :
637          in.consume("false") ? false :
638          in.consume("null") ? nullptr :
639          in.consume("Infinity") ?
640           (in.getOpts().parse_numbers_as_strings ? (dynamic)"Infinity" :
641             (dynamic)std::numeric_limits<double>::infinity()) :
642          in.consume("NaN") ?
643            (in.getOpts().parse_numbers_as_strings ? (dynamic)"NaN" :
644              (dynamic)std::numeric_limits<double>::quiet_NaN()) :
645          in.error("expected json value");
646 }
647
648 }
649
650 //////////////////////////////////////////////////////////////////////
651
652 fbstring serialize(dynamic const& dyn, serialization_opts const& opts) {
653   fbstring ret;
654   unsigned indentLevel = 0;
655   Printer p(ret, opts.pretty_formatting ? &indentLevel : nullptr, &opts);
656   p(dyn);
657   return ret;
658 }
659
660 // Escape a string so that it is legal to print it in JSON text.
661 void escapeString(StringPiece input,
662                   fbstring& out,
663                   const serialization_opts& opts) {
664   auto hexDigit = [] (int c) -> char {
665     return c < 10 ? c + '0' : c - 10 + 'a';
666   };
667
668   out.reserve(out.size() + input.size() + 2);
669   out.push_back('\"');
670
671   auto* p = reinterpret_cast<const unsigned char*>(input.begin());
672   auto* q = reinterpret_cast<const unsigned char*>(input.begin());
673   auto* e = reinterpret_cast<const unsigned char*>(input.end());
674
675   while (p < e) {
676     // Since non-ascii encoding inherently does utf8 validation
677     // we explicitly validate utf8 only if non-ascii encoding is disabled.
678     if ((opts.validate_utf8 || opts.skip_invalid_utf8)
679         && !opts.encode_non_ascii) {
680       // to achieve better spatial and temporal coherence
681       // we do utf8 validation progressively along with the
682       // string-escaping instead of two separate passes
683
684       // as the encoding progresses, q will stay at or ahead of p
685       CHECK(q >= p);
686
687       // as p catches up with q, move q forward
688       if (q == p) {
689         // calling utf8_decode has the side effect of
690         // checking that utf8 encodings are valid
691         char32_t v = decodeUtf8(q, e, opts.skip_invalid_utf8);
692         if (opts.skip_invalid_utf8 && v == U'\ufffd') {
693           out.append("\ufffd");
694           p = q;
695           continue;
696         }
697       }
698     }
699     if (opts.encode_non_ascii && (*p & 0x80)) {
700       // note that this if condition captures utf8 chars
701       // with value > 127, so size > 1 byte
702       char32_t v = decodeUtf8(p, e, opts.skip_invalid_utf8);
703       out.append("\\u");
704       out.push_back(hexDigit(v >> 12));
705       out.push_back(hexDigit((v >> 8) & 0x0f));
706       out.push_back(hexDigit((v >> 4) & 0x0f));
707       out.push_back(hexDigit(v & 0x0f));
708     } else if (*p == '\\' || *p == '\"') {
709       out.push_back('\\');
710       out.push_back(*p++);
711     } else if (*p <= 0x1f) {
712       switch (*p) {
713         case '\b': out.append("\\b"); p++; break;
714         case '\f': out.append("\\f"); p++; break;
715         case '\n': out.append("\\n"); p++; break;
716         case '\r': out.append("\\r"); p++; break;
717         case '\t': out.append("\\t"); p++; break;
718         default:
719           // note that this if condition captures non readable chars
720           // with value < 32, so size = 1 byte (e.g control chars).
721           out.append("\\u00");
722           out.push_back(hexDigit((*p & 0xf0) >> 4));
723           out.push_back(hexDigit(*p & 0xf));
724           p++;
725       }
726     } else {
727       out.push_back(*p++);
728     }
729   }
730
731   out.push_back('\"');
732 }
733
734 fbstring stripComments(StringPiece jsonC) {
735   fbstring result;
736   enum class State {
737     None,
738     InString,
739     InlineComment,
740     LineComment
741   } state = State::None;
742
743   for (size_t i = 0; i < jsonC.size(); ++i) {
744     auto s = jsonC.subpiece(i);
745     switch (state) {
746       case State::None:
747         if (s.startsWith("/*")) {
748           state = State::InlineComment;
749           ++i;
750           continue;
751         } else if (s.startsWith("//")) {
752           state = State::LineComment;
753           ++i;
754           continue;
755         } else if (s[0] == '\"') {
756           state = State::InString;
757         }
758         result.push_back(s[0]);
759         break;
760       case State::InString:
761         if (s[0] == '\\') {
762           if (UNLIKELY(s.size() == 1)) {
763             throw std::logic_error("Invalid JSONC: string is not terminated");
764           }
765           result.push_back(s[0]);
766           result.push_back(s[1]);
767           ++i;
768           continue;
769         } else if (s[0] == '\"') {
770           state = State::None;
771         }
772         result.push_back(s[0]);
773         break;
774       case State::InlineComment:
775         if (s.startsWith("*/")) {
776           state = State::None;
777           ++i;
778         }
779         break;
780       case State::LineComment:
781         if (s[0] == '\n') {
782           // skip the line break. It doesn't matter.
783           state = State::None;
784         }
785         break;
786       default:
787         throw std::logic_error("Unknown comment state");
788     }
789   }
790   return result;
791 }
792
793 }
794
795 //////////////////////////////////////////////////////////////////////
796
797 dynamic parseJson(StringPiece range) {
798   return parseJson(range, json::serialization_opts());
799 }
800
801 dynamic parseJson(
802     StringPiece range,
803     json::serialization_opts const& opts) {
804
805   json::Input in(range, &opts);
806
807   auto ret = parseValue(in);
808   in.skipWhitespace();
809   if (in.size() && *in != '\0') {
810     in.error("parsing didn't consume all input");
811   }
812   return ret;
813 }
814
815 fbstring toJson(dynamic const& dyn) {
816   return json::serialize(dyn, json::serialization_opts());
817 }
818
819 fbstring toPrettyJson(dynamic const& dyn) {
820   json::serialization_opts opts;
821   opts.pretty_formatting = true;
822   return json::serialize(dyn, opts);
823 }
824
825 //////////////////////////////////////////////////////////////////////
826 // dynamic::print_as_pseudo_json() is implemented here for header
827 // ordering reasons (most of the dynamic implementation is in
828 // dynamic-inl.h, which we don't want to include json.h).
829
830 void dynamic::print_as_pseudo_json(std::ostream& out) const {
831   json::serialization_opts opts;
832   opts.allow_non_string_keys = true;
833   opts.allow_nan_inf = true;
834   out << json::serialize(*this, opts);
835 }
836
837 void PrintTo(const dynamic& dyn, std::ostream* os) {
838   json::serialization_opts opts;
839   opts.allow_nan_inf = true;
840   opts.allow_non_string_keys = true;
841   opts.pretty_formatting = true;
842   opts.sort_keys = true;
843   *os << json::serialize(dyn, opts);
844 }
845
846 //////////////////////////////////////////////////////////////////////
847
848 }