xrpld
Loading...
Searching...
No Matches
json_writer.cpp
1#include <xrpl/json/json_writer.h>
2
3#include <xrpl/beast/utility/instrumentation.h>
4#include <xrpl/json/json_forwards.h>
5#include <xrpl/json/json_value.h>
6
7#include <cstdio>
8#include <cstring>
9#include <iomanip>
10#include <ios>
11#include <ostream>
12#include <sstream>
13#include <string>
14#include <utility>
15
16namespace json {
17
18static bool
20{
21 return ch > 0 && ch <= 0x1F;
22}
23
24static bool
26{
27 while (*str != 0)
28 {
29 if (isControlCharacter(*(str++)))
30 return true;
31 }
32
33 return false;
34}
35static void
36uintToString(unsigned int value, char*& current)
37{
38 *--current = 0;
39
40 do
41 {
42 *--current = (value % 10) + '0';
43 value /= 10;
44 } while (value != 0);
45}
46
49{
50 char buffer[32];
51 char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness)
52 bool const isNegative = value < 0;
53
54 if (isNegative)
55 value = -value;
56
57 uintToString(UInt(value), current);
58
59 if (isNegative)
60 *--current = '-';
61
62 XRPL_ASSERT(current >= buffer, "json::valueToString(Int) : buffer check");
63 return current;
64}
65
68{
69 char buffer[32];
70 char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness)
71 uintToString(value, current);
72 XRPL_ASSERT(current >= buffer, "json::valueToString(UInt) : buffer check");
73 return current;
74}
75
77valueToString(double value)
78{
79 // Allocate a buffer that is more than large enough to store the 16 digits
80 // of precision requested below.
81 char buffer[32];
82 // Print into the buffer. We need not request the alternative representation
83 // that always has a decimal point because JSON doesn't distinguish the
84 // concepts of reals and integers.
85#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005
86 // to avoid warning.
87 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
88#else
89 snprintf(buffer, sizeof(buffer), "%.16g", value);
90#endif
91 return buffer;
92}
93
95valueToString(bool value)
96{
97 return value ? "true" : "false";
98}
99
101valueToQuotedString(char const* value)
102{
103 // Not sure how to handle unicode...
104 if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr && !containsControlCharacter(value))
105 return std::string("\"") + value + "\"";
106
107 // We have to walk value and escape any special characters.
108 // Appending to std::string is not efficient, but this should be rare.
109 // (Note: forward slashes are *not* rare, but I am not escaping them.)
110 unsigned const maxsize = (strlen(value) * 2) + 3; // all-escaped+quotes+NULL
111 std::string result;
112 result.reserve(maxsize); // to avoid lots of mallocs
113 result += "\"";
114
115 for (char const* c = value; *c != 0; ++c)
116 {
117 switch (*c)
118 {
119 case '\"':
120 result += "\\\"";
121 break;
122
123 case '\\':
124 result += "\\\\";
125 break;
126
127 case '\b':
128 result += "\\b";
129 break;
130
131 case '\f':
132 result += "\\f";
133 break;
134
135 case '\n':
136 result += "\\n";
137 break;
138
139 case '\r':
140 result += "\\r";
141 break;
142
143 case '\t':
144 result += "\\t";
145 break;
146
147 // case '/':
148 // Even though \/ is considered a legal escape in JSON, a bare
149 // slash is also legal, so I see no reason to escape it.
150 // (I hope I am not misunderstanding something.
151 // blep notes: actually escaping \/ may be useful in javascript
152 // to avoid </ sequence. Should add a flag to allow this
153 // compatibility mode and prevent this sequence from occurring.
154 default:
155 if (isControlCharacter(*c))
156 {
158 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4)
159 << static_cast<int>(*c);
160 result += oss.str();
161 }
162 else
163 {
164 result += *c;
165 }
166
167 break;
168 }
169 }
170
171 result += "\"";
172 return result;
173}
174
175// Class FastWriter
176// //////////////////////////////////////////////////////////////////
177
180{
181 document_ = "";
182 writeValue(root);
183 return std::move(document_);
184}
185
186void
188{
189 switch (value.type())
190 {
191 case ValueType::Null:
192 document_ += "null";
193 break;
194
195 case ValueType::Int:
196 document_ += valueToString(value.asInt());
197 break;
198
199 case ValueType::UInt:
200 document_ += valueToString(value.asUInt());
201 break;
202
203 case ValueType::Real:
204 document_ += valueToString(value.asDouble());
205 break;
206
208 document_ += valueToQuotedString(value.asCString());
209 break;
210
212 document_ += valueToString(value.asBool());
213 break;
214
215 case ValueType::Array: {
216 document_ += "[";
217 int const size = value.size();
218
219 for (int index = 0; index < size; ++index)
220 {
221 if (index > 0)
222 document_ += ",";
223
224 writeValue(value[index]);
225 }
226
227 document_ += "]";
228 }
229 break;
230
231 case ValueType::Object: {
232 Value::Members members(value.getMemberNames());
233 document_ += "{";
234
235 for (Value::Members::iterator it = members.begin(); it != members.end(); ++it)
236 {
237 std::string const& name = *it;
238
239 if (it != members.begin())
240 document_ += ",";
241
243 document_ += ":";
244 writeValue(value[name]);
245 }
246
247 document_ += "}";
248 }
249 break;
250 }
251}
252
253// Class StyledWriter
254// //////////////////////////////////////////////////////////////////
255
257
260{
261 document_ = "";
262 addChildValues_ = false;
263 indentString_ = "";
264 writeValue(root);
265 document_ += "\n";
266 return document_;
267}
268
269void
271{
272 switch (value.type())
273 {
274 case ValueType::Null:
275 pushValue("null");
276 break;
277
278 case ValueType::Int:
279 pushValue(valueToString(value.asInt()));
280 break;
281
282 case ValueType::UInt:
283 pushValue(valueToString(value.asUInt()));
284 break;
285
286 case ValueType::Real:
287 pushValue(valueToString(value.asDouble()));
288 break;
289
291 pushValue(valueToQuotedString(value.asCString()));
292 break;
293
295 pushValue(valueToString(value.asBool()));
296 break;
297
298 case ValueType::Array:
299 writeArrayValue(value);
300 break;
301
302 case ValueType::Object: {
303 Value::Members members(value.getMemberNames());
304
305 if (members.empty())
306 {
307 pushValue("{}");
308 }
309 else
310 {
311 writeWithIndent("{");
312 indent();
313 Value::Members::iterator it = members.begin();
314
315 while (true)
316 {
317 std::string const& name = *it;
318 Value const& childValue = value[name];
320 document_ += " : ";
321 writeValue(childValue);
322
323 if (++it; it == members.end())
324 break;
325
326 document_ += ",";
327 }
328
329 unindent();
330 writeWithIndent("}");
331 }
332 }
333 break;
334 }
335}
336
337void
339{
340 unsigned const size = value.size();
341
342 if (size == 0)
343 {
344 pushValue("[]");
345 }
346 else
347 {
348 bool const isArrayMultiLine = isMultilineArray(value);
349
350 if (isArrayMultiLine)
351 {
352 writeWithIndent("[");
353 indent();
354 bool const hasChildValue = !childValues_.empty();
355 unsigned index = 0;
356
357 while (true)
358 {
359 Value const& childValue = value[index];
360
361 if (hasChildValue)
362 {
364 }
365 else
366 {
367 writeIndent();
368 writeValue(childValue);
369 }
370
371 if (++index == size)
372 break;
373
374 document_ += ",";
375 }
376
377 unindent();
378 writeWithIndent("]");
379 }
380 else // output on a single line
381 {
382 XRPL_ASSERT(
383 childValues_.size() == size,
384 "json::StyledWriter::writeArrayValue : child size match");
385 document_ += "[ ";
386
387 for (unsigned index = 0; index < size; ++index)
388 {
389 if (index > 0)
390 document_ += ", ";
391
392 document_ += childValues_[index];
393 }
394
395 document_ += " ]";
396 }
397 }
398}
399
400bool
402{
403 int const size = value.size();
404 bool isMultiLine = size * 3 >= rightMargin_;
405 childValues_.clear();
406
407 for (int index = 0; index < size && !isMultiLine; ++index)
408 {
409 Value const& childValue = value[index];
410 isMultiLine = isMultiLine ||
411 ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);
412 }
413
414 if (!isMultiLine) // check if line length > max line length
415 {
416 childValues_.reserve(size);
417 addChildValues_ = true;
418 int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]'
419
420 for (int index = 0; index < size; ++index)
421 {
422 writeValue(value[index]);
423 lineLength += int(childValues_[index].length());
424 }
425
426 addChildValues_ = false;
427 isMultiLine = isMultiLine || lineLength >= rightMargin_;
428 }
429
430 return isMultiLine;
431}
432
433void
435{
436 if (addChildValues_)
437 {
438 childValues_.push_back(value);
439 }
440 else
441 {
442 document_ += value;
443 }
444}
445
446void
448{
449 if (!document_.empty())
450 {
451 char const last = document_[document_.length() - 1];
452
453 if (last == ' ') // already indented
454 return;
455
456 if (last != '\n') // Comments may add new-line
457 document_ += '\n';
458 }
459
461}
462
463void
465{
466 writeIndent();
467 document_ += value;
468}
469
470void
475
476void
478{
479 XRPL_ASSERT(
480 int(indentString_.size()) >= indentSize_,
481 "json::StyledWriter::unindent : maximum indent size");
482 indentString_.resize(indentString_.size() - indentSize_);
483}
484
485// Class StyledStreamWriter
486// //////////////////////////////////////////////////////////////////
487
489 : indentation_(std::move(indentation))
490{
491}
492
493void
495{
496 document_ = &out;
497 addChildValues_ = false;
498 indentString_ = "";
499 writeValue(root);
500 *document_ << "\n";
501 document_ = nullptr; // Forget the stream, for safety.
502}
503
504void
506{
507 switch (value.type())
508 {
509 case ValueType::Null:
510 pushValue("null");
511 break;
512
513 case ValueType::Int:
514 pushValue(valueToString(value.asInt()));
515 break;
516
517 case ValueType::UInt:
518 pushValue(valueToString(value.asUInt()));
519 break;
520
521 case ValueType::Real:
522 pushValue(valueToString(value.asDouble()));
523 break;
524
526 pushValue(valueToQuotedString(value.asCString()));
527 break;
528
530 pushValue(valueToString(value.asBool()));
531 break;
532
533 case ValueType::Array:
534 writeArrayValue(value);
535 break;
536
537 case ValueType::Object: {
538 Value::Members members(value.getMemberNames());
539
540 if (members.empty())
541 {
542 pushValue("{}");
543 }
544 else
545 {
546 writeWithIndent("{");
547 indent();
548 Value::Members::iterator it = members.begin();
549
550 while (true)
551 {
552 std::string const& name = *it;
553 Value const& childValue = value[name];
555 *document_ << " : ";
556 writeValue(childValue);
557
558 if (++it == members.end())
559 break;
560
561 *document_ << ",";
562 }
563
564 unindent();
565 writeWithIndent("}");
566 }
567 }
568 break;
569 }
570}
571
572void
574{
575 unsigned const size = value.size();
576
577 if (size == 0)
578 {
579 pushValue("[]");
580 }
581 else
582 {
583 bool const isArrayMultiLine = isMultilineArray(value);
584
585 if (isArrayMultiLine)
586 {
587 writeWithIndent("[");
588 indent();
589 bool const hasChildValue = !childValues_.empty();
590 unsigned index = 0;
591
592 while (true)
593 {
594 Value const& childValue = value[index];
595
596 if (hasChildValue)
597 {
599 }
600 else
601 {
602 writeIndent();
603 writeValue(childValue);
604 }
605
606 if (++index == size)
607 break;
608
609 *document_ << ",";
610 }
611
612 unindent();
613 writeWithIndent("]");
614 }
615 else // output on a single line
616 {
617 XRPL_ASSERT(
618 childValues_.size() == size,
619 "json::StyledStreamWriter::writeArrayValue : child size match");
620 *document_ << "[ ";
621
622 for (unsigned index = 0; index < size; ++index)
623 {
624 if (index > 0)
625 *document_ << ", ";
626
627 *document_ << childValues_[index];
628 }
629
630 *document_ << " ]";
631 }
632 }
633}
634
635bool
637{
638 int const size = value.size();
639 bool isMultiLine = size * 3 >= rightMargin_;
640 childValues_.clear();
641
642 for (int index = 0; index < size && !isMultiLine; ++index)
643 {
644 Value const& childValue = value[index];
645 isMultiLine = isMultiLine ||
646 ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);
647 }
648
649 if (!isMultiLine) // check if line length > max line length
650 {
651 childValues_.reserve(size);
652 addChildValues_ = true;
653 int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]'
654
655 for (int index = 0; index < size; ++index)
656 {
657 writeValue(value[index]);
658 lineLength += int(childValues_[index].length());
659 }
660
661 addChildValues_ = false;
662 isMultiLine = isMultiLine || lineLength >= rightMargin_;
663 }
664
665 return isMultiLine;
666}
667
668void
670{
671 if (addChildValues_)
672 {
673 childValues_.push_back(value);
674 }
675 else
676 {
677 *document_ << value;
678 }
679}
680
681void
683{
684 /*
685 Some comments in this method would have been nice. ;-)
686
687 if ( !document_.empty() )
688 {
689 char last = document_[document_.length()-1];
690 if ( last == ' ' ) // already indented
691 return;
692 if ( last != '\n' ) // Comments may add new-line
693 *document_ << '\n';
694 }
695 */
696 *document_ << '\n' << indentString_;
697}
698
699void
701{
702 writeIndent();
703 *document_ << value;
704}
705
706void
711
712void
714{
715 XRPL_ASSERT(
716 indentString_.size() >= indentation_.size(),
717 "json::StyledStreamWriter::unindent : maximum indent size");
718 indentString_.resize(indentString_.size() - indentation_.size());
719}
720
722operator<<(std::ostream& sout, Value const& root)
723{
725 writer.write(sout, root);
726 return sout;
727}
728
729} // namespace json
T begin(T... args)
T c_str(T... args)
void writeValue(Value const &value)
std::string write(Value const &root) override
std::string document_
Definition json_writer.h:45
Writes a Value in JSON format in a human friendly way, to a stream rather than to a string.
void pushValue(std::string const &value)
void writeArrayValue(Value const &value)
std::ostream * document_
void write(std::ostream &out, Value const &root)
Serialize a Value in JSON format.
void writeValue(Value const &value)
bool isMultilineArray(Value const &value)
StyledStreamWriter(std::string indentation="\t")
void writeWithIndent(std::string const &value)
void pushValue(std::string const &value)
ChildValues childValues_
bool isMultilineArray(Value const &value)
std::string write(Value const &root) override
Serialize a Value in JSON format.
std::string indentString_
void writeValue(Value const &value)
void writeWithIndent(std::string const &value)
void writeArrayValue(Value const &value)
std::string document_
Represents a JSON value.
Definition json_value.h:130
bool isObject() const
std::vector< std::string > Members
Definition json_value.h:134
bool isArray() const
UInt size() const
Number of values in array or object.
T empty(T... args)
T end(T... args)
T hex(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
int Int
std::string valueToQuotedString(char const *value)
static bool containsControlCharacter(char const *str)
unsigned int UInt
static bool isControlCharacter(char ch)
@ UInt
unsigned integer value
Definition json_value.h:21
@ Int
signed integer value
Definition json_value.h:20
@ String
UTF-8 string value.
Definition json_value.h:23
@ Boolean
bool value
Definition json_value.h:24
@ Array
array value (ordered list)
Definition json_value.h:25
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
@ Real
double value
Definition json_value.h:22
@ Null
'null' value
Definition json_value.h:19
static void uintToString(unsigned int value, char *&current)
std::ostream & operator<<(std::ostream &, Value const &root)
Output using the StyledStreamWriter.
std::string valueToString(Int value)
STL namespace.
T reserve(T... args)
T setfill(T... args)
T setw(T... args)
T str(T... args)
T uppercase(T... args)