rippled
Loading...
Searching...
No Matches
json_writer.cpp
1#include <xrpl/beast/utility/instrumentation.h>
2#include <xrpl/json/json_forwards.h>
3#include <xrpl/json/json_value.h>
4#include <xrpl/json/json_writer.h>
5
6#include <cstdio>
7#include <cstring>
8#include <iomanip>
9#include <ios>
10#include <ostream>
11#include <sstream>
12#include <string>
13#include <utility>
14
15namespace Json {
16
17static bool
19{
20 return ch > 0 && ch <= 0x1F;
21}
22
23static bool
25{
26 while (*str != 0)
27 {
28 if (isControlCharacter(*(str++)))
29 return true;
30 }
31
32 return false;
33}
34static void
35uintToString(unsigned int value, char*& current)
36{
37 *--current = 0;
38
39 do
40 {
41 *--current = (value % 10) + '0';
42 value /= 10;
43 } while (value != 0);
44}
45
48{
49 char buffer[32];
50 char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness)
51 bool const isNegative = value < 0;
52
53 if (isNegative)
54 value = -value;
55
56 uintToString(UInt(value), current);
57
58 if (isNegative)
59 *--current = '-';
60
61 XRPL_ASSERT(current >= buffer, "Json::valueToString(Int) : buffer check");
62 return current;
63}
64
67{
68 char buffer[32];
69 char* current = buffer + sizeof(buffer); // NOLINT(misc-const-correctness)
70 uintToString(value, current);
71 XRPL_ASSERT(current >= buffer, "Json::valueToString(UInt) : buffer check");
72 return current;
73}
74
76valueToString(double value)
77{
78 // Allocate a buffer that is more than large enough to store the 16 digits
79 // of precision requested below.
80 char buffer[32];
81 // Print into the buffer. We need not request the alternative representation
82 // that always has a decimal point because JSON doesn't distinguish the
83 // concepts of reals and integers.
84#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005
85 // to avoid warning.
86 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
87#else
88 snprintf(buffer, sizeof(buffer), "%.16g", value);
89#endif
90 return buffer;
91}
92
94valueToString(bool value)
95{
96 return value ? "true" : "false";
97}
98
100valueToQuotedString(char const* value)
101{
102 // Not sure how to handle unicode...
103 if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr && !containsControlCharacter(value))
104 return std::string("\"") + value + "\"";
105
106 // We have to walk value and escape any special characters.
107 // Appending to std::string is not efficient, but this should be rare.
108 // (Note: forward slashes are *not* rare, but I am not escaping them.)
109 unsigned const maxsize = (strlen(value) * 2) + 3; // all-escaped+quotes+NULL
110 std::string result;
111 result.reserve(maxsize); // to avoid lots of mallocs
112 result += "\"";
113
114 for (char const* c = value; *c != 0; ++c)
115 {
116 switch (*c)
117 {
118 case '\"':
119 result += "\\\"";
120 break;
121
122 case '\\':
123 result += "\\\\";
124 break;
125
126 case '\b':
127 result += "\\b";
128 break;
129
130 case '\f':
131 result += "\\f";
132 break;
133
134 case '\n':
135 result += "\\n";
136 break;
137
138 case '\r':
139 result += "\\r";
140 break;
141
142 case '\t':
143 result += "\\t";
144 break;
145
146 // case '/':
147 // Even though \/ is considered a legal escape in JSON, a bare
148 // slash is also legal, so I see no reason to escape it.
149 // (I hope I am not misunderstanding something.
150 // blep notes: actually escaping \/ may be useful in javascript
151 // to avoid </ sequence. Should add a flag to allow this
152 // compatibility mode and prevent this sequence from occurring.
153 default:
154 if (isControlCharacter(*c))
155 {
157 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4)
158 << static_cast<int>(*c);
159 result += oss.str();
160 }
161 else
162 {
163 result += *c;
164 }
165
166 break;
167 }
168 }
169
170 result += "\"";
171 return result;
172}
173
174// Class FastWriter
175// //////////////////////////////////////////////////////////////////
176
179{
180 document_ = "";
181 writeValue(root);
182 return std::move(document_);
183}
184
185void
187{
188 switch (value.type())
189 {
190 case nullValue:
191 document_ += "null";
192 break;
193
194 case intValue:
195 document_ += valueToString(value.asInt());
196 break;
197
198 case uintValue:
199 document_ += valueToString(value.asUInt());
200 break;
201
202 case realValue:
203 document_ += valueToString(value.asDouble());
204 break;
205
206 case stringValue:
208 break;
209
210 case booleanValue:
211 document_ += valueToString(value.asBool());
212 break;
213
214 case arrayValue: {
215 document_ += "[";
216 int const size = value.size();
217
218 for (int index = 0; index < size; ++index)
219 {
220 if (index > 0)
221 document_ += ",";
222
223 writeValue(value[index]);
224 }
225
226 document_ += "]";
227 }
228 break;
229
230 case objectValue: {
231 Value::Members members(value.getMemberNames());
232 document_ += "{";
233
234 for (Value::Members::iterator it = members.begin(); it != members.end(); ++it)
235 {
236 std::string const& name = *it;
237
238 if (it != members.begin())
239 document_ += ",";
240
242 document_ += ":";
243 writeValue(value[name]);
244 }
245
246 document_ += "}";
247 }
248 break;
249 }
250}
251
252// Class StyledWriter
253// //////////////////////////////////////////////////////////////////
254
258
261{
262 document_ = "";
263 addChildValues_ = false;
264 indentString_ = "";
265 writeValue(root);
266 document_ += "\n";
267 return document_;
268}
269
270void
272{
273 switch (value.type())
274 {
275 case nullValue:
276 pushValue("null");
277 break;
278
279 case intValue:
280 pushValue(valueToString(value.asInt()));
281 break;
282
283 case uintValue:
285 break;
286
287 case realValue:
289 break;
290
291 case stringValue:
293 break;
294
295 case booleanValue:
297 break;
298
299 case arrayValue:
300 writeArrayValue(value);
301 break;
302
303 case objectValue: {
304 Value::Members members(value.getMemberNames());
305
306 if (members.empty())
307 {
308 pushValue("{}");
309 }
310 else
311 {
312 writeWithIndent("{");
313 indent();
314 Value::Members::iterator it = members.begin();
315
316 while (true)
317 {
318 std::string const& name = *it;
319 Value const& childValue = value[name];
321 document_ += " : ";
322 writeValue(childValue);
323
324 if (++it; it == members.end())
325 break;
326
327 document_ += ",";
328 }
329
330 unindent();
331 writeWithIndent("}");
332 }
333 }
334 break;
335 }
336}
337
338void
340{
341 unsigned const size = value.size();
342
343 if (size == 0)
344 {
345 pushValue("[]");
346 }
347 else
348 {
349 bool const isArrayMultiLine = isMultilineArray(value);
350
351 if (isArrayMultiLine)
352 {
353 writeWithIndent("[");
354 indent();
355 bool const hasChildValue = !childValues_.empty();
356 unsigned index = 0;
357
358 while (true)
359 {
360 Value const& childValue = value[index];
361
362 if (hasChildValue)
363 {
365 }
366 else
367 {
368 writeIndent();
369 writeValue(childValue);
370 }
371
372 if (++index == size)
373 break;
374
375 document_ += ",";
376 }
377
378 unindent();
379 writeWithIndent("]");
380 }
381 else // output on a single line
382 {
383 XRPL_ASSERT(
384 childValues_.size() == size,
385 "Json::StyledWriter::writeArrayValue : child size match");
386 document_ += "[ ";
387
388 for (unsigned index = 0; index < size; ++index)
389 {
390 if (index > 0)
391 document_ += ", ";
392
393 document_ += childValues_[index];
394 }
395
396 document_ += " ]";
397 }
398 }
399}
400
401bool
403{
404 int const size = value.size();
405 bool isMultiLine = size * 3 >= rightMargin_;
407
408 for (int index = 0; index < size && !isMultiLine; ++index)
409 {
410 Value const& childValue = value[index];
411 isMultiLine = isMultiLine ||
412 ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);
413 }
414
415 if (!isMultiLine) // check if line length > max line length
416 {
417 childValues_.reserve(size);
418 addChildValues_ = true;
419 int lineLength = 4 + ((size - 1) * 2); // '[ ' + ', '*n + ' ]'
420
421 for (int index = 0; index < size; ++index)
422 {
423 writeValue(value[index]);
424 lineLength += int(childValues_[index].length());
425 }
426
427 addChildValues_ = false;
428 isMultiLine = isMultiLine || lineLength >= rightMargin_;
429 }
430
431 return isMultiLine;
432}
433
434void
436{
437 if (addChildValues_)
438 {
439 childValues_.push_back(value);
440 }
441 else
442 {
443 document_ += value;
444 }
445}
446
447void
449{
450 if (!document_.empty())
451 {
452 char const last = document_[document_.length() - 1];
453
454 if (last == ' ') // already indented
455 return;
456
457 if (last != '\n') // Comments may add new-line
458 document_ += '\n';
459 }
460
462}
463
464void
466{
467 writeIndent();
468 document_ += value;
469}
470
471void
476
477void
479{
480 XRPL_ASSERT(
482 "Json::StyledWriter::unindent : maximum indent size");
484}
485
486// Class StyledStreamWriter
487// //////////////////////////////////////////////////////////////////
488
489StyledStreamWriter::StyledStreamWriter(std::string indentation) : indentation_(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 nullValue:
510 pushValue("null");
511 break;
512
513 case intValue:
514 pushValue(valueToString(value.asInt()));
515 break;
516
517 case uintValue:
519 break;
520
521 case realValue:
523 break;
524
525 case stringValue:
527 break;
528
529 case booleanValue:
531 break;
532
533 case arrayValue:
534 writeArrayValue(value);
535 break;
536
537 case objectValue: {
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_;
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(
717 "Json::StyledStreamWriter::unindent : maximum indent 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)
std::string write(Value const &root) override
void writeValue(Value const &value)
std::string document_
Definition json_writer.h:49
Writes a Value in JSON format in a human friendly way, to a stream rather than to a string.
void write(std::ostream &out, Value const &root)
Serialize a Value in JSON format.
void writeWithIndent(std::string const &value)
void writeValue(Value const &value)
bool isMultilineArray(Value const &value)
void writeArrayValue(Value const &value)
std::ostream * document_
StyledStreamWriter(std::string indentation="\t")
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 pushValue(std::string const &value)
void writeValue(Value const &value)
void writeArrayValue(Value const &value)
std::string document_
void writeWithIndent(std::string const &value)
Represents a JSON value.
Definition json_value.h:130
bool isArray() const
UInt size() const
Number of values in array or object.
char const * asCString() const
Int asInt() const
UInt asUInt() const
Members getMemberNames() const
Return a list of the member names.
ValueType type() const
bool isObject() const
bool asBool() const
double asDouble() const
T clear(T... args)
T empty(T... args)
T end(T... args)
T hex(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
static bool isControlCharacter(char ch)
static void uintToString(unsigned int value, char *&current)
std::string valueToString(Int value)
static bool containsControlCharacter(char const *str)
std::string valueToQuotedString(char const *value)
@ booleanValue
bool value
Definition json_value.h:24
@ nullValue
'null' value
Definition json_value.h:19
@ stringValue
UTF-8 string value.
Definition json_value.h:23
@ realValue
double value
Definition json_value.h:22
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ intValue
signed integer value
Definition json_value.h:20
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
@ uintValue
unsigned integer value
Definition json_value.h:21
int Int
unsigned int UInt
std::ostream & operator<<(std::ostream &, Value const &root)
Output using the StyledStreamWriter.
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T setfill(T... args)
T setw(T... args)
T size(T... args)
T str(T... args)
T uppercase(T... args)