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)
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);
51 bool 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);
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 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 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
255StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3)
256{
257}
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 pushValue("{}");
308 else
309 {
310 writeWithIndent("{");
311 indent();
312 Value::Members::iterator it = members.begin();
313
314 while (true)
315 {
316 std::string const& name = *it;
317 Value const& childValue = value[name];
319 document_ += " : ";
320 writeValue(childValue);
321
322 if (++it == members.end())
323 break;
324
325 document_ += ",";
326 }
327
328 unindent();
329 writeWithIndent("}");
330 }
331 }
332 break;
333 }
334}
335
336void
338{
339 unsigned size = value.size();
340
341 if (size == 0)
342 pushValue("[]");
343 else
344 {
345 bool isArrayMultiLine = isMultilineArray(value);
346
347 if (isArrayMultiLine)
348 {
349 writeWithIndent("[");
350 indent();
351 bool hasChildValue = !childValues_.empty();
352 unsigned index = 0;
353
354 while (true)
355 {
356 Value const& childValue = value[index];
357
358 if (hasChildValue)
360 else
361 {
362 writeIndent();
363 writeValue(childValue);
364 }
365
366 if (++index == size)
367 break;
368
369 document_ += ",";
370 }
371
372 unindent();
373 writeWithIndent("]");
374 }
375 else // output on a single line
376 {
377 XRPL_ASSERT(childValues_.size() == size, "Json::StyledWriter::writeArrayValue : child size match");
378 document_ += "[ ";
379
380 for (unsigned index = 0; index < size; ++index)
381 {
382 if (index > 0)
383 document_ += ", ";
384
385 document_ += childValues_[index];
386 }
387
388 document_ += " ]";
389 }
390 }
391}
392
393bool
395{
396 int size = value.size();
397 bool isMultiLine = size * 3 >= rightMargin_;
399
400 for (int index = 0; index < size && !isMultiLine; ++index)
401 {
402 Value const& childValue = value[index];
403 isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);
404 }
405
406 if (!isMultiLine) // check if line length > max line length
407 {
408 childValues_.reserve(size);
409 addChildValues_ = true;
410 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
411
412 for (int index = 0; index < size; ++index)
413 {
414 writeValue(value[index]);
415 lineLength += int(childValues_[index].length());
416 }
417
418 addChildValues_ = false;
419 isMultiLine = isMultiLine || lineLength >= rightMargin_;
420 }
421
422 return isMultiLine;
423}
424
425void
427{
428 if (addChildValues_)
429 childValues_.push_back(value);
430 else
431 document_ += value;
432}
433
434void
436{
437 if (!document_.empty())
438 {
439 char last = document_[document_.length() - 1];
440
441 if (last == ' ') // already indented
442 return;
443
444 if (last != '\n') // Comments may add new-line
445 document_ += '\n';
446 }
447
449}
450
451void
453{
454 writeIndent();
455 document_ += value;
456}
457
458void
463
464void
466{
467 XRPL_ASSERT(int(indentString_.size()) >= indentSize_, "Json::StyledWriter::unindent : maximum indent size");
469}
470
471// Class StyledStreamWriter
472// //////////////////////////////////////////////////////////////////
473
475 : document_(nullptr), rightMargin_(74), indentation_(indentation)
476{
477}
478
479void
481{
482 document_ = &out;
483 addChildValues_ = false;
484 indentString_ = "";
485 writeValue(root);
486 *document_ << "\n";
487 document_ = nullptr; // Forget the stream, for safety.
488}
489
490void
492{
493 switch (value.type())
494 {
495 case nullValue:
496 pushValue("null");
497 break;
498
499 case intValue:
500 pushValue(valueToString(value.asInt()));
501 break;
502
503 case uintValue:
505 break;
506
507 case realValue:
509 break;
510
511 case stringValue:
513 break;
514
515 case booleanValue:
517 break;
518
519 case arrayValue:
520 writeArrayValue(value);
521 break;
522
523 case objectValue: {
524 Value::Members members(value.getMemberNames());
525
526 if (members.empty())
527 pushValue("{}");
528 else
529 {
530 writeWithIndent("{");
531 indent();
532 Value::Members::iterator it = members.begin();
533
534 while (true)
535 {
536 std::string const& name = *it;
537 Value const& childValue = value[name];
539 *document_ << " : ";
540 writeValue(childValue);
541
542 if (++it == members.end())
543 break;
544
545 *document_ << ",";
546 }
547
548 unindent();
549 writeWithIndent("}");
550 }
551 }
552 break;
553 }
554}
555
556void
558{
559 unsigned size = value.size();
560
561 if (size == 0)
562 pushValue("[]");
563 else
564 {
565 bool isArrayMultiLine = isMultilineArray(value);
566
567 if (isArrayMultiLine)
568 {
569 writeWithIndent("[");
570 indent();
571 bool hasChildValue = !childValues_.empty();
572 unsigned index = 0;
573
574 while (true)
575 {
576 Value const& childValue = value[index];
577
578 if (hasChildValue)
580 else
581 {
582 writeIndent();
583 writeValue(childValue);
584 }
585
586 if (++index == size)
587 break;
588
589 *document_ << ",";
590 }
591
592 unindent();
593 writeWithIndent("]");
594 }
595 else // output on a single line
596 {
597 XRPL_ASSERT(childValues_.size() == size, "Json::StyledStreamWriter::writeArrayValue : child size match");
598 *document_ << "[ ";
599
600 for (unsigned index = 0; index < size; ++index)
601 {
602 if (index > 0)
603 *document_ << ", ";
604
605 *document_ << childValues_[index];
606 }
607
608 *document_ << " ]";
609 }
610 }
611}
612
613bool
615{
616 int size = value.size();
617 bool isMultiLine = size * 3 >= rightMargin_;
619
620 for (int index = 0; index < size && !isMultiLine; ++index)
621 {
622 Value const& childValue = value[index];
623 isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);
624 }
625
626 if (!isMultiLine) // check if line length > max line length
627 {
628 childValues_.reserve(size);
629 addChildValues_ = true;
630 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
631
632 for (int index = 0; index < size; ++index)
633 {
634 writeValue(value[index]);
635 lineLength += int(childValues_[index].length());
636 }
637
638 addChildValues_ = false;
639 isMultiLine = isMultiLine || lineLength >= rightMargin_;
640 }
641
642 return isMultiLine;
643}
644
645void
647{
648 if (addChildValues_)
649 childValues_.push_back(value);
650 else
651 *document_ << value;
652}
653
654void
656{
657 /*
658 Some comments in this method would have been nice. ;-)
659
660 if ( !document_.empty() )
661 {
662 char last = document_[document_.length()-1];
663 if ( last == ' ' ) // already indented
664 return;
665 if ( last != '\n' ) // Comments may add new-line
666 *document_ << '\n';
667 }
668 */
669 *document_ << '\n' << indentString_;
670}
671
672void
674{
675 writeIndent();
676 *document_ << value;
677}
678
679void
684
685void
687{
688 XRPL_ASSERT(
689 indentString_.size() >= indentation_.size(), "Json::StyledStreamWriter::unindent : maximum indent size");
691}
692
694operator<<(std::ostream& sout, Value const& root)
695{
697 writer.write(sout, root);
698 return sout;
699}
700
701} // 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:50
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:131
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:6
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:25
@ nullValue
'null' value
Definition json_value.h:20
@ stringValue
UTF-8 string value.
Definition json_value.h:24
@ realValue
double value
Definition json_value.h:23
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ intValue
signed integer value
Definition json_value.h:21
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
@ uintValue
unsigned integer value
Definition json_value.h:22
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)