Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
3158 views
1
// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
2
// Distributed under MIT license, or public domain if desired and
3
// recognized in your jurisdiction.
4
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5
6
#if !defined(JSON_IS_AMALGAMATION)
7
#include "json_tool.h"
8
#include <json/writer.h>
9
#endif // if !defined(JSON_IS_AMALGAMATION)
10
#include <algorithm>
11
#include <cassert>
12
#include <cctype>
13
#include <cstring>
14
#include <iomanip>
15
#include <memory>
16
#include <set>
17
#include <sstream>
18
#include <utility>
19
20
#if __cplusplus >= 201103L
21
#include <cmath>
22
#include <cstdio>
23
24
#if !defined(isnan)
25
#define isnan std::isnan
26
#endif
27
28
#if !defined(isfinite)
29
#define isfinite std::isfinite
30
#endif
31
32
#else
33
#include <cmath>
34
#include <cstdio>
35
36
#if defined(_MSC_VER)
37
#if !defined(isnan)
38
#include <float.h>
39
#define isnan _isnan
40
#endif
41
42
#if !defined(isfinite)
43
#include <float.h>
44
#define isfinite _finite
45
#endif
46
47
#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
48
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
49
#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
50
51
#endif //_MSC_VER
52
53
#if defined(__sun) && defined(__SVR4) // Solaris
54
#if !defined(isfinite)
55
#include <ieeefp.h>
56
#define isfinite finite
57
#endif
58
#endif
59
60
#if defined(__hpux)
61
#if !defined(isfinite)
62
#if defined(__ia64) && !defined(finite)
63
#define isfinite(x) \
64
((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
65
#endif
66
#endif
67
#endif
68
69
#if !defined(isnan)
70
// IEEE standard states that NaN values will not compare to themselves
71
#define isnan(x) ((x) != (x))
72
#endif
73
74
#if !defined(__APPLE__)
75
#if !defined(isfinite)
76
#define isfinite finite
77
#endif
78
#endif
79
#endif
80
81
#if defined(_MSC_VER)
82
// Disable warning about strdup being deprecated.
83
#pragma warning(disable : 4996)
84
#endif
85
86
namespace Json {
87
88
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
89
using StreamWriterPtr = std::unique_ptr<StreamWriter>;
90
#else
91
using StreamWriterPtr = std::auto_ptr<StreamWriter>;
92
#endif
93
94
String valueToString(LargestInt value) {
95
UIntToStringBuffer buffer;
96
char* current = buffer + sizeof(buffer);
97
if (value == Value::minLargestInt) {
98
uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
99
*--current = '-';
100
} else if (value < 0) {
101
uintToString(LargestUInt(-value), current);
102
*--current = '-';
103
} else {
104
uintToString(LargestUInt(value), current);
105
}
106
assert(current >= buffer);
107
return current;
108
}
109
110
String valueToString(LargestUInt value) {
111
UIntToStringBuffer buffer;
112
char* current = buffer + sizeof(buffer);
113
uintToString(value, current);
114
assert(current >= buffer);
115
return current;
116
}
117
118
#if defined(JSON_HAS_INT64)
119
120
String valueToString(Int value) { return valueToString(LargestInt(value)); }
121
122
String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
123
124
#endif // # if defined(JSON_HAS_INT64)
125
126
namespace {
127
String valueToString(double value, bool useSpecialFloats,
128
unsigned int precision, PrecisionType precisionType) {
129
// Print into the buffer. We need not request the alternative representation
130
// that always has a decimal point because JSON doesn't distinguish the
131
// concepts of reals and integers.
132
if (!isfinite(value)) {
133
static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
134
{"null", "-1e+9999", "1e+9999"}};
135
return reps[useSpecialFloats ? 0 : 1][isnan(value) ? 0
136
: (value < 0) ? 1
137
: 2];
138
}
139
140
String buffer(size_t(36), '\0');
141
while (true) {
142
int len = jsoncpp_snprintf(
143
&*buffer.begin(), buffer.size(),
144
(precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
145
precision, value);
146
assert(len >= 0);
147
auto wouldPrint = static_cast<size_t>(len);
148
if (wouldPrint >= buffer.size()) {
149
buffer.resize(wouldPrint + 1);
150
continue;
151
}
152
buffer.resize(wouldPrint);
153
break;
154
}
155
156
buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
157
158
// try to ensure we preserve the fact that this was given to us as a double on
159
// input
160
if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
161
buffer += ".0";
162
}
163
164
// strip the zero padding from the right
165
if (precisionType == PrecisionType::decimalPlaces) {
166
buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
167
buffer.end());
168
}
169
170
return buffer;
171
}
172
} // namespace
173
174
String valueToString(double value, unsigned int precision,
175
PrecisionType precisionType) {
176
return valueToString(value, false, precision, precisionType);
177
}
178
179
String valueToString(bool value) { return value ? "true" : "false"; }
180
181
static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
182
assert(s || !n);
183
184
return std::any_of(s, s + n, [](unsigned char c) {
185
return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
186
});
187
}
188
189
static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
190
const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
191
192
unsigned int firstByte = static_cast<unsigned char>(*s);
193
194
if (firstByte < 0x80)
195
return firstByte;
196
197
if (firstByte < 0xE0) {
198
if (e - s < 2)
199
return REPLACEMENT_CHARACTER;
200
201
unsigned int calculated =
202
((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
203
s += 1;
204
// oversized encoded characters are invalid
205
return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
206
}
207
208
if (firstByte < 0xF0) {
209
if (e - s < 3)
210
return REPLACEMENT_CHARACTER;
211
212
unsigned int calculated = ((firstByte & 0x0F) << 12) |
213
((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
214
(static_cast<unsigned int>(s[2]) & 0x3F);
215
s += 2;
216
// surrogates aren't valid codepoints itself
217
// shouldn't be UTF-8 encoded
218
if (calculated >= 0xD800 && calculated <= 0xDFFF)
219
return REPLACEMENT_CHARACTER;
220
// oversized encoded characters are invalid
221
return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
222
}
223
224
if (firstByte < 0xF8) {
225
if (e - s < 4)
226
return REPLACEMENT_CHARACTER;
227
228
unsigned int calculated = ((firstByte & 0x07) << 18) |
229
((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
230
((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
231
(static_cast<unsigned int>(s[3]) & 0x3F);
232
s += 3;
233
// oversized encoded characters are invalid
234
return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
235
}
236
237
return REPLACEMENT_CHARACTER;
238
}
239
240
static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
241
"101112131415161718191a1b1c1d1e1f"
242
"202122232425262728292a2b2c2d2e2f"
243
"303132333435363738393a3b3c3d3e3f"
244
"404142434445464748494a4b4c4d4e4f"
245
"505152535455565758595a5b5c5d5e5f"
246
"606162636465666768696a6b6c6d6e6f"
247
"707172737475767778797a7b7c7d7e7f"
248
"808182838485868788898a8b8c8d8e8f"
249
"909192939495969798999a9b9c9d9e9f"
250
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
251
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
252
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
253
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
254
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
255
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
256
257
static String toHex16Bit(unsigned int x) {
258
const unsigned int hi = (x >> 8) & 0xff;
259
const unsigned int lo = x & 0xff;
260
String result(4, ' ');
261
result[0] = hex2[2 * hi];
262
result[1] = hex2[2 * hi + 1];
263
result[2] = hex2[2 * lo];
264
result[3] = hex2[2 * lo + 1];
265
return result;
266
}
267
268
static void appendRaw(String& result, unsigned ch) {
269
result += static_cast<char>(ch);
270
}
271
272
static void appendHex(String& result, unsigned ch) {
273
result.append("\\u").append(toHex16Bit(ch));
274
}
275
276
static String valueToQuotedStringN(const char* value, size_t length,
277
bool emitUTF8 = false) {
278
if (value == nullptr)
279
return "";
280
281
if (!doesAnyCharRequireEscaping(value, length))
282
return String("\"") + value + "\"";
283
// We have to walk value and escape any special characters.
284
// Appending to String is not efficient, but this should be rare.
285
// (Note: forward slashes are *not* rare, but I am not escaping them.)
286
String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
287
String result;
288
result.reserve(maxsize); // to avoid lots of mallocs
289
result += "\"";
290
char const* end = value + length;
291
for (const char* c = value; c != end; ++c) {
292
switch (*c) {
293
case '\"':
294
result += "\\\"";
295
break;
296
case '\\':
297
result += "\\\\";
298
break;
299
case '\b':
300
result += "\\b";
301
break;
302
case '\f':
303
result += "\\f";
304
break;
305
case '\n':
306
result += "\\n";
307
break;
308
case '\r':
309
result += "\\r";
310
break;
311
case '\t':
312
result += "\\t";
313
break;
314
// case '/':
315
// Even though \/ is considered a legal escape in JSON, a bare
316
// slash is also legal, so I see no reason to escape it.
317
// (I hope I am not misunderstanding something.)
318
// blep notes: actually escaping \/ may be useful in javascript to avoid </
319
// sequence.
320
// Should add a flag to allow this compatibility mode and prevent this
321
// sequence from occurring.
322
default: {
323
if (emitUTF8) {
324
unsigned codepoint = static_cast<unsigned char>(*c);
325
if (codepoint < 0x20) {
326
appendHex(result, codepoint);
327
} else {
328
appendRaw(result, codepoint);
329
}
330
} else {
331
unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
332
if (codepoint < 0x20) {
333
appendHex(result, codepoint);
334
} else if (codepoint < 0x80) {
335
appendRaw(result, codepoint);
336
} else if (codepoint < 0x10000) {
337
// Basic Multilingual Plane
338
appendHex(result, codepoint);
339
} else {
340
// Extended Unicode. Encode 20 bits as a surrogate pair.
341
codepoint -= 0x10000;
342
appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
343
appendHex(result, 0xdc00 + (codepoint & 0x3ff));
344
}
345
}
346
} break;
347
}
348
}
349
result += "\"";
350
return result;
351
}
352
353
String valueToQuotedString(const char* value) {
354
return valueToQuotedStringN(value, strlen(value));
355
}
356
357
String valueToQuotedString(const char* value, size_t length) {
358
return valueToQuotedStringN(value, length);
359
}
360
361
// Class Writer
362
// //////////////////////////////////////////////////////////////////
363
Writer::~Writer() = default;
364
365
// Class FastWriter
366
// //////////////////////////////////////////////////////////////////
367
368
FastWriter::FastWriter()
369
370
= default;
371
372
void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
373
374
void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
375
376
void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
377
378
String FastWriter::write(const Value& root) {
379
document_.clear();
380
writeValue(root);
381
if (!omitEndingLineFeed_)
382
document_ += '\n';
383
return document_;
384
}
385
386
void FastWriter::writeValue(const Value& value) {
387
switch (value.type()) {
388
case nullValue:
389
if (!dropNullPlaceholders_)
390
document_ += "null";
391
break;
392
case intValue:
393
document_ += valueToString(value.asLargestInt());
394
break;
395
case uintValue:
396
document_ += valueToString(value.asLargestUInt());
397
break;
398
case realValue:
399
document_ += valueToString(value.asDouble());
400
break;
401
case stringValue: {
402
// Is NULL possible for value.string_? No.
403
char const* str;
404
char const* end;
405
bool ok = value.getString(&str, &end);
406
if (ok)
407
document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
408
break;
409
}
410
case booleanValue:
411
document_ += valueToString(value.asBool());
412
break;
413
case arrayValue: {
414
document_ += '[';
415
ArrayIndex size = value.size();
416
for (ArrayIndex index = 0; index < size; ++index) {
417
if (index > 0)
418
document_ += ',';
419
writeValue(value[index]);
420
}
421
document_ += ']';
422
} break;
423
case objectValue: {
424
Value::Members members(value.getMemberNames());
425
document_ += '{';
426
for (auto it = members.begin(); it != members.end(); ++it) {
427
const String& name = *it;
428
if (it != members.begin())
429
document_ += ',';
430
document_ += valueToQuotedStringN(name.data(), name.length());
431
document_ += yamlCompatibilityEnabled_ ? ": " : ":";
432
writeValue(value[name]);
433
}
434
document_ += '}';
435
} break;
436
}
437
}
438
439
// Class StyledWriter
440
// //////////////////////////////////////////////////////////////////
441
442
StyledWriter::StyledWriter() = default;
443
444
String StyledWriter::write(const Value& root) {
445
document_.clear();
446
addChildValues_ = false;
447
indentString_.clear();
448
writeCommentBeforeValue(root);
449
writeValue(root);
450
writeCommentAfterValueOnSameLine(root);
451
document_ += '\n';
452
return document_;
453
}
454
455
void StyledWriter::writeValue(const Value& value) {
456
switch (value.type()) {
457
case nullValue:
458
pushValue("null");
459
break;
460
case intValue:
461
pushValue(valueToString(value.asLargestInt()));
462
break;
463
case uintValue:
464
pushValue(valueToString(value.asLargestUInt()));
465
break;
466
case realValue:
467
pushValue(valueToString(value.asDouble()));
468
break;
469
case stringValue: {
470
// Is NULL possible for value.string_? No.
471
char const* str;
472
char const* end;
473
bool ok = value.getString(&str, &end);
474
if (ok)
475
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
476
else
477
pushValue("");
478
break;
479
}
480
case booleanValue:
481
pushValue(valueToString(value.asBool()));
482
break;
483
case arrayValue:
484
writeArrayValue(value);
485
break;
486
case objectValue: {
487
Value::Members members(value.getMemberNames());
488
if (members.empty())
489
pushValue("{}");
490
else {
491
writeWithIndent("{");
492
indent();
493
auto it = members.begin();
494
for (;;) {
495
const String& name = *it;
496
const Value& childValue = value[name];
497
writeCommentBeforeValue(childValue);
498
writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
499
document_ += " : ";
500
writeValue(childValue);
501
if (++it == members.end()) {
502
writeCommentAfterValueOnSameLine(childValue);
503
break;
504
}
505
document_ += ',';
506
writeCommentAfterValueOnSameLine(childValue);
507
}
508
unindent();
509
writeWithIndent("}");
510
}
511
} break;
512
}
513
}
514
515
void StyledWriter::writeArrayValue(const Value& value) {
516
size_t size = value.size();
517
if (size == 0)
518
pushValue("[]");
519
else {
520
bool isArrayMultiLine = isMultilineArray(value);
521
if (isArrayMultiLine) {
522
writeWithIndent("[");
523
indent();
524
bool hasChildValue = !childValues_.empty();
525
ArrayIndex index = 0;
526
for (;;) {
527
const Value& childValue = value[index];
528
writeCommentBeforeValue(childValue);
529
if (hasChildValue)
530
writeWithIndent(childValues_[index]);
531
else {
532
writeIndent();
533
writeValue(childValue);
534
}
535
if (++index == size) {
536
writeCommentAfterValueOnSameLine(childValue);
537
break;
538
}
539
document_ += ',';
540
writeCommentAfterValueOnSameLine(childValue);
541
}
542
unindent();
543
writeWithIndent("]");
544
} else // output on a single line
545
{
546
assert(childValues_.size() == size);
547
document_ += "[ ";
548
for (size_t index = 0; index < size; ++index) {
549
if (index > 0)
550
document_ += ", ";
551
document_ += childValues_[index];
552
}
553
document_ += " ]";
554
}
555
}
556
}
557
558
bool StyledWriter::isMultilineArray(const Value& value) {
559
ArrayIndex const size = value.size();
560
bool isMultiLine = size * 3 >= rightMargin_;
561
childValues_.clear();
562
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
563
const Value& childValue = value[index];
564
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
565
!childValue.empty());
566
}
567
if (!isMultiLine) // check if line length > max line length
568
{
569
childValues_.reserve(size);
570
addChildValues_ = true;
571
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
572
for (ArrayIndex index = 0; index < size; ++index) {
573
if (hasCommentForValue(value[index])) {
574
isMultiLine = true;
575
}
576
writeValue(value[index]);
577
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
578
}
579
addChildValues_ = false;
580
isMultiLine = isMultiLine || lineLength >= rightMargin_;
581
}
582
return isMultiLine;
583
}
584
585
void StyledWriter::pushValue(const String& value) {
586
if (addChildValues_)
587
childValues_.push_back(value);
588
else
589
document_ += value;
590
}
591
592
void StyledWriter::writeIndent() {
593
if (!document_.empty()) {
594
char last = document_[document_.length() - 1];
595
if (last == ' ') // already indented
596
return;
597
if (last != '\n') // Comments may add new-line
598
document_ += '\n';
599
}
600
document_ += indentString_;
601
}
602
603
void StyledWriter::writeWithIndent(const String& value) {
604
writeIndent();
605
document_ += value;
606
}
607
608
void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
609
610
void StyledWriter::unindent() {
611
assert(indentString_.size() >= indentSize_);
612
indentString_.resize(indentString_.size() - indentSize_);
613
}
614
615
void StyledWriter::writeCommentBeforeValue(const Value& root) {
616
if (!root.hasComment(commentBefore))
617
return;
618
619
document_ += '\n';
620
writeIndent();
621
const String& comment = root.getComment(commentBefore);
622
String::const_iterator iter = comment.begin();
623
while (iter != comment.end()) {
624
document_ += *iter;
625
if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
626
writeIndent();
627
++iter;
628
}
629
630
// Comments are stripped of trailing newlines, so add one here
631
document_ += '\n';
632
}
633
634
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
635
if (root.hasComment(commentAfterOnSameLine))
636
document_ += " " + root.getComment(commentAfterOnSameLine);
637
638
if (root.hasComment(commentAfter)) {
639
document_ += '\n';
640
document_ += root.getComment(commentAfter);
641
document_ += '\n';
642
}
643
}
644
645
bool StyledWriter::hasCommentForValue(const Value& value) {
646
return value.hasComment(commentBefore) ||
647
value.hasComment(commentAfterOnSameLine) ||
648
value.hasComment(commentAfter);
649
}
650
651
// Class StyledStreamWriter
652
// //////////////////////////////////////////////////////////////////
653
654
StyledStreamWriter::StyledStreamWriter(String indentation)
655
: document_(nullptr), indentation_(std::move(indentation)),
656
addChildValues_(), indented_(false) {}
657
658
void StyledStreamWriter::write(OStream& out, const Value& root) {
659
document_ = &out;
660
addChildValues_ = false;
661
indentString_.clear();
662
indented_ = true;
663
writeCommentBeforeValue(root);
664
if (!indented_)
665
writeIndent();
666
indented_ = true;
667
writeValue(root);
668
writeCommentAfterValueOnSameLine(root);
669
*document_ << "\n";
670
document_ = nullptr; // Forget the stream, for safety.
671
}
672
673
void StyledStreamWriter::writeValue(const Value& value) {
674
switch (value.type()) {
675
case nullValue:
676
pushValue("null");
677
break;
678
case intValue:
679
pushValue(valueToString(value.asLargestInt()));
680
break;
681
case uintValue:
682
pushValue(valueToString(value.asLargestUInt()));
683
break;
684
case realValue:
685
pushValue(valueToString(value.asDouble()));
686
break;
687
case stringValue: {
688
// Is NULL possible for value.string_? No.
689
char const* str;
690
char const* end;
691
bool ok = value.getString(&str, &end);
692
if (ok)
693
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
694
else
695
pushValue("");
696
break;
697
}
698
case booleanValue:
699
pushValue(valueToString(value.asBool()));
700
break;
701
case arrayValue:
702
writeArrayValue(value);
703
break;
704
case objectValue: {
705
Value::Members members(value.getMemberNames());
706
if (members.empty())
707
pushValue("{}");
708
else {
709
writeWithIndent("{");
710
indent();
711
auto it = members.begin();
712
for (;;) {
713
const String& name = *it;
714
const Value& childValue = value[name];
715
writeCommentBeforeValue(childValue);
716
writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
717
*document_ << " : ";
718
writeValue(childValue);
719
if (++it == members.end()) {
720
writeCommentAfterValueOnSameLine(childValue);
721
break;
722
}
723
*document_ << ",";
724
writeCommentAfterValueOnSameLine(childValue);
725
}
726
unindent();
727
writeWithIndent("}");
728
}
729
} break;
730
}
731
}
732
733
void StyledStreamWriter::writeArrayValue(const Value& value) {
734
unsigned size = value.size();
735
if (size == 0)
736
pushValue("[]");
737
else {
738
bool isArrayMultiLine = isMultilineArray(value);
739
if (isArrayMultiLine) {
740
writeWithIndent("[");
741
indent();
742
bool hasChildValue = !childValues_.empty();
743
unsigned index = 0;
744
for (;;) {
745
const Value& childValue = value[index];
746
writeCommentBeforeValue(childValue);
747
if (hasChildValue)
748
writeWithIndent(childValues_[index]);
749
else {
750
if (!indented_)
751
writeIndent();
752
indented_ = true;
753
writeValue(childValue);
754
indented_ = false;
755
}
756
if (++index == size) {
757
writeCommentAfterValueOnSameLine(childValue);
758
break;
759
}
760
*document_ << ",";
761
writeCommentAfterValueOnSameLine(childValue);
762
}
763
unindent();
764
writeWithIndent("]");
765
} else // output on a single line
766
{
767
assert(childValues_.size() == size);
768
*document_ << "[ ";
769
for (unsigned index = 0; index < size; ++index) {
770
if (index > 0)
771
*document_ << ", ";
772
*document_ << childValues_[index];
773
}
774
*document_ << " ]";
775
}
776
}
777
}
778
779
bool StyledStreamWriter::isMultilineArray(const Value& value) {
780
ArrayIndex const size = value.size();
781
bool isMultiLine = size * 3 >= rightMargin_;
782
childValues_.clear();
783
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
784
const Value& childValue = value[index];
785
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
786
!childValue.empty());
787
}
788
if (!isMultiLine) // check if line length > max line length
789
{
790
childValues_.reserve(size);
791
addChildValues_ = true;
792
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
793
for (ArrayIndex index = 0; index < size; ++index) {
794
if (hasCommentForValue(value[index])) {
795
isMultiLine = true;
796
}
797
writeValue(value[index]);
798
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
799
}
800
addChildValues_ = false;
801
isMultiLine = isMultiLine || lineLength >= rightMargin_;
802
}
803
return isMultiLine;
804
}
805
806
void StyledStreamWriter::pushValue(const String& value) {
807
if (addChildValues_)
808
childValues_.push_back(value);
809
else
810
*document_ << value;
811
}
812
813
void StyledStreamWriter::writeIndent() {
814
// blep intended this to look at the so-far-written string
815
// to determine whether we are already indented, but
816
// with a stream we cannot do that. So we rely on some saved state.
817
// The caller checks indented_.
818
*document_ << '\n' << indentString_;
819
}
820
821
void StyledStreamWriter::writeWithIndent(const String& value) {
822
if (!indented_)
823
writeIndent();
824
*document_ << value;
825
indented_ = false;
826
}
827
828
void StyledStreamWriter::indent() { indentString_ += indentation_; }
829
830
void StyledStreamWriter::unindent() {
831
assert(indentString_.size() >= indentation_.size());
832
indentString_.resize(indentString_.size() - indentation_.size());
833
}
834
835
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
836
if (!root.hasComment(commentBefore))
837
return;
838
839
if (!indented_)
840
writeIndent();
841
const String& comment = root.getComment(commentBefore);
842
String::const_iterator iter = comment.begin();
843
while (iter != comment.end()) {
844
*document_ << *iter;
845
if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
846
// writeIndent(); // would include newline
847
*document_ << indentString_;
848
++iter;
849
}
850
indented_ = false;
851
}
852
853
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
854
if (root.hasComment(commentAfterOnSameLine))
855
*document_ << ' ' << root.getComment(commentAfterOnSameLine);
856
857
if (root.hasComment(commentAfter)) {
858
writeIndent();
859
*document_ << root.getComment(commentAfter);
860
}
861
indented_ = false;
862
}
863
864
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
865
return value.hasComment(commentBefore) ||
866
value.hasComment(commentAfterOnSameLine) ||
867
value.hasComment(commentAfter);
868
}
869
870
//////////////////////////
871
// BuiltStyledStreamWriter
872
873
/// Scoped enums are not available until C++11.
874
struct CommentStyle {
875
/// Decide whether to write comments.
876
enum Enum {
877
None, ///< Drop all comments.
878
Most, ///< Recover odd behavior of previous versions (not implemented yet).
879
All ///< Keep all comments.
880
};
881
};
882
883
struct BuiltStyledStreamWriter : public StreamWriter {
884
BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
885
String colonSymbol, String nullSymbol,
886
String endingLineFeedSymbol, bool useSpecialFloats,
887
bool emitUTF8, unsigned int precision,
888
PrecisionType precisionType);
889
int write(Value const& root, OStream* sout) override;
890
891
private:
892
void writeValue(Value const& value);
893
void writeArrayValue(Value const& value);
894
bool isMultilineArray(Value const& value);
895
void pushValue(String const& value);
896
void writeIndent();
897
void writeWithIndent(String const& value);
898
void indent();
899
void unindent();
900
void writeCommentBeforeValue(Value const& root);
901
void writeCommentAfterValueOnSameLine(Value const& root);
902
static bool hasCommentForValue(const Value& value);
903
904
using ChildValues = std::vector<String>;
905
906
ChildValues childValues_;
907
String indentString_;
908
unsigned int rightMargin_;
909
String indentation_;
910
CommentStyle::Enum cs_;
911
String colonSymbol_;
912
String nullSymbol_;
913
String endingLineFeedSymbol_;
914
bool addChildValues_ : 1;
915
bool indented_ : 1;
916
bool useSpecialFloats_ : 1;
917
bool emitUTF8_ : 1;
918
unsigned int precision_;
919
PrecisionType precisionType_;
920
};
921
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
922
String indentation, CommentStyle::Enum cs, String colonSymbol,
923
String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
924
bool emitUTF8, unsigned int precision, PrecisionType precisionType)
925
: rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
926
colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
927
endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
928
addChildValues_(false), indented_(false),
929
useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
930
precision_(precision), precisionType_(precisionType) {}
931
int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
932
sout_ = sout;
933
addChildValues_ = false;
934
indented_ = true;
935
indentString_.clear();
936
writeCommentBeforeValue(root);
937
if (!indented_)
938
writeIndent();
939
indented_ = true;
940
writeValue(root);
941
writeCommentAfterValueOnSameLine(root);
942
*sout_ << endingLineFeedSymbol_;
943
sout_ = nullptr;
944
return 0;
945
}
946
void BuiltStyledStreamWriter::writeValue(Value const& value) {
947
switch (value.type()) {
948
case nullValue:
949
pushValue(nullSymbol_);
950
break;
951
case intValue:
952
pushValue(valueToString(value.asLargestInt()));
953
break;
954
case uintValue:
955
pushValue(valueToString(value.asLargestUInt()));
956
break;
957
case realValue:
958
pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
959
precisionType_));
960
break;
961
case stringValue: {
962
// Is NULL is possible for value.string_? No.
963
char const* str;
964
char const* end;
965
bool ok = value.getString(&str, &end);
966
if (ok)
967
pushValue(
968
valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
969
else
970
pushValue("");
971
break;
972
}
973
case booleanValue:
974
pushValue(valueToString(value.asBool()));
975
break;
976
case arrayValue:
977
writeArrayValue(value);
978
break;
979
case objectValue: {
980
Value::Members members(value.getMemberNames());
981
if (members.empty())
982
pushValue("{}");
983
else {
984
writeWithIndent("{");
985
indent();
986
auto it = members.begin();
987
for (;;) {
988
String const& name = *it;
989
Value const& childValue = value[name];
990
writeCommentBeforeValue(childValue);
991
writeWithIndent(
992
valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
993
*sout_ << colonSymbol_;
994
writeValue(childValue);
995
if (++it == members.end()) {
996
writeCommentAfterValueOnSameLine(childValue);
997
break;
998
}
999
*sout_ << ",";
1000
writeCommentAfterValueOnSameLine(childValue);
1001
}
1002
unindent();
1003
writeWithIndent("}");
1004
}
1005
} break;
1006
}
1007
}
1008
1009
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
1010
unsigned size = value.size();
1011
if (size == 0)
1012
pushValue("[]");
1013
else {
1014
bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
1015
if (isMultiLine) {
1016
writeWithIndent("[");
1017
indent();
1018
bool hasChildValue = !childValues_.empty();
1019
unsigned index = 0;
1020
for (;;) {
1021
Value const& childValue = value[index];
1022
writeCommentBeforeValue(childValue);
1023
if (hasChildValue)
1024
writeWithIndent(childValues_[index]);
1025
else {
1026
if (!indented_)
1027
writeIndent();
1028
indented_ = true;
1029
writeValue(childValue);
1030
indented_ = false;
1031
}
1032
if (++index == size) {
1033
writeCommentAfterValueOnSameLine(childValue);
1034
break;
1035
}
1036
*sout_ << ",";
1037
writeCommentAfterValueOnSameLine(childValue);
1038
}
1039
unindent();
1040
writeWithIndent("]");
1041
} else // output on a single line
1042
{
1043
assert(childValues_.size() == size);
1044
*sout_ << "[";
1045
if (!indentation_.empty())
1046
*sout_ << " ";
1047
for (unsigned index = 0; index < size; ++index) {
1048
if (index > 0)
1049
*sout_ << ((!indentation_.empty()) ? ", " : ",");
1050
*sout_ << childValues_[index];
1051
}
1052
if (!indentation_.empty())
1053
*sout_ << " ";
1054
*sout_ << "]";
1055
}
1056
}
1057
}
1058
1059
bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1060
ArrayIndex const size = value.size();
1061
bool isMultiLine = size * 3 >= rightMargin_;
1062
childValues_.clear();
1063
for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1064
Value const& childValue = value[index];
1065
isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1066
!childValue.empty());
1067
}
1068
if (!isMultiLine) // check if line length > max line length
1069
{
1070
childValues_.reserve(size);
1071
addChildValues_ = true;
1072
ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1073
for (ArrayIndex index = 0; index < size; ++index) {
1074
if (hasCommentForValue(value[index])) {
1075
isMultiLine = true;
1076
}
1077
writeValue(value[index]);
1078
lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1079
}
1080
addChildValues_ = false;
1081
isMultiLine = isMultiLine || lineLength >= rightMargin_;
1082
}
1083
return isMultiLine;
1084
}
1085
1086
void BuiltStyledStreamWriter::pushValue(String const& value) {
1087
if (addChildValues_)
1088
childValues_.push_back(value);
1089
else
1090
*sout_ << value;
1091
}
1092
1093
void BuiltStyledStreamWriter::writeIndent() {
1094
// blep intended this to look at the so-far-written string
1095
// to determine whether we are already indented, but
1096
// with a stream we cannot do that. So we rely on some saved state.
1097
// The caller checks indented_.
1098
1099
if (!indentation_.empty()) {
1100
// In this case, drop newlines too.
1101
*sout_ << '\n' << indentString_;
1102
}
1103
}
1104
1105
void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1106
if (!indented_)
1107
writeIndent();
1108
*sout_ << value;
1109
indented_ = false;
1110
}
1111
1112
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1113
1114
void BuiltStyledStreamWriter::unindent() {
1115
assert(indentString_.size() >= indentation_.size());
1116
indentString_.resize(indentString_.size() - indentation_.size());
1117
}
1118
1119
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1120
if (cs_ == CommentStyle::None)
1121
return;
1122
if (!root.hasComment(commentBefore))
1123
return;
1124
1125
if (!indented_)
1126
writeIndent();
1127
const String& comment = root.getComment(commentBefore);
1128
String::const_iterator iter = comment.begin();
1129
while (iter != comment.end()) {
1130
*sout_ << *iter;
1131
if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
1132
// writeIndent(); // would write extra newline
1133
*sout_ << indentString_;
1134
++iter;
1135
}
1136
indented_ = false;
1137
}
1138
1139
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
1140
Value const& root) {
1141
if (cs_ == CommentStyle::None)
1142
return;
1143
if (root.hasComment(commentAfterOnSameLine))
1144
*sout_ << " " + root.getComment(commentAfterOnSameLine);
1145
1146
if (root.hasComment(commentAfter)) {
1147
writeIndent();
1148
*sout_ << root.getComment(commentAfter);
1149
}
1150
}
1151
1152
// static
1153
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1154
return value.hasComment(commentBefore) ||
1155
value.hasComment(commentAfterOnSameLine) ||
1156
value.hasComment(commentAfter);
1157
}
1158
1159
///////////////
1160
// StreamWriter
1161
1162
StreamWriter::StreamWriter() : sout_(nullptr) {}
1163
StreamWriter::~StreamWriter() = default;
1164
StreamWriter::Factory::~Factory() = default;
1165
StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
1166
StreamWriterBuilder::~StreamWriterBuilder() = default;
1167
StreamWriter* StreamWriterBuilder::newStreamWriter() const {
1168
const String indentation = settings_["indentation"].asString();
1169
const String cs_str = settings_["commentStyle"].asString();
1170
const String pt_str = settings_["precisionType"].asString();
1171
const bool eyc = settings_["enableYAMLCompatibility"].asBool();
1172
const bool dnp = settings_["dropNullPlaceholders"].asBool();
1173
const bool usf = settings_["useSpecialFloats"].asBool();
1174
const bool emitUTF8 = settings_["emitUTF8"].asBool();
1175
unsigned int pre = settings_["precision"].asUInt();
1176
CommentStyle::Enum cs = CommentStyle::All;
1177
if (cs_str == "All") {
1178
cs = CommentStyle::All;
1179
} else if (cs_str == "None") {
1180
cs = CommentStyle::None;
1181
} else {
1182
throwRuntimeError("commentStyle must be 'All' or 'None'");
1183
}
1184
PrecisionType precisionType(significantDigits);
1185
if (pt_str == "significant") {
1186
precisionType = PrecisionType::significantDigits;
1187
} else if (pt_str == "decimal") {
1188
precisionType = PrecisionType::decimalPlaces;
1189
} else {
1190
throwRuntimeError("precisionType must be 'significant' or 'decimal'");
1191
}
1192
String colonSymbol = " : ";
1193
if (eyc) {
1194
colonSymbol = ": ";
1195
} else if (indentation.empty()) {
1196
colonSymbol = ":";
1197
}
1198
String nullSymbol = "null";
1199
if (dnp) {
1200
nullSymbol.clear();
1201
}
1202
if (pre > 17)
1203
pre = 17;
1204
String endingLineFeedSymbol;
1205
return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1206
endingLineFeedSymbol, usf, emitUTF8, pre,
1207
precisionType);
1208
}
1209
1210
bool StreamWriterBuilder::validate(Json::Value* invalid) const {
1211
static const auto& valid_keys = *new std::set<String>{
1212
"indentation",
1213
"commentStyle",
1214
"enableYAMLCompatibility",
1215
"dropNullPlaceholders",
1216
"useSpecialFloats",
1217
"emitUTF8",
1218
"precision",
1219
"precisionType",
1220
};
1221
for (auto si = settings_.begin(); si != settings_.end(); ++si) {
1222
auto key = si.name();
1223
if (valid_keys.count(key))
1224
continue;
1225
if (invalid)
1226
(*invalid)[key] = *si;
1227
else
1228
return false;
1229
}
1230
return invalid ? invalid->empty() : true;
1231
}
1232
1233
Value& StreamWriterBuilder::operator[](const String& key) {
1234
return settings_[key];
1235
}
1236
// static
1237
void StreamWriterBuilder::setDefaults(Json::Value* settings) {
1238
//! [StreamWriterBuilderDefaults]
1239
(*settings)["commentStyle"] = "All";
1240
(*settings)["indentation"] = "\t";
1241
(*settings)["enableYAMLCompatibility"] = false;
1242
(*settings)["dropNullPlaceholders"] = false;
1243
(*settings)["useSpecialFloats"] = false;
1244
(*settings)["emitUTF8"] = false;
1245
(*settings)["precision"] = 17;
1246
(*settings)["precisionType"] = "significant";
1247
//! [StreamWriterBuilderDefaults]
1248
}
1249
1250
String writeString(StreamWriter::Factory const& factory, Value const& root) {
1251
OStringStream sout;
1252
StreamWriterPtr const writer(factory.newStreamWriter());
1253
writer->write(root, &sout);
1254
return std::move(sout).str();
1255
}
1256
1257
OStream& operator<<(OStream& sout, Value const& root) {
1258
StreamWriterBuilder builder;
1259
StreamWriterPtr const writer(builder.newStreamWriter());
1260
writer->write(root, &sout);
1261
return sout;
1262
}
1263
1264
} // namespace Json
1265
1266