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 distingish the
83 // concepts of reals and integers.
84#if defined(_MSC_VER) && \
85 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 &&
106 return std::string("\"") + value + "\"";
107
108 // We have to walk value and escape any special characters.
109 // Appending to std::string is not efficient, but this should be rare.
110 // (Note: forward slashes are *not* rare, but I am not escaping them.)
111 unsigned maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL
112 std::string result;
113 result.reserve(maxsize); // to avoid lots of mallocs
114 result += "\"";
115
116 for (char const* c = value; *c != 0; ++c)
117 {
118 switch (*c)
119 {
120 case '\"':
121 result += "\\\"";
122 break;
123
124 case '\\':
125 result += "\\\\";
126 break;
127
128 case '\b':
129 result += "\\b";
130 break;
131
132 case '\f':
133 result += "\\f";
134 break;
135
136 case '\n':
137 result += "\\n";
138 break;
139
140 case '\r':
141 result += "\\r";
142 break;
143
144 case '\t':
145 result += "\\t";
146 break;
147
148 // case '/':
149 // Even though \/ is considered a legal escape in JSON, a bare
150 // slash is also legal, so I see no reason to escape it.
151 // (I hope I am not misunderstanding something.
152 // blep notes: actually escaping \/ may be useful in javascript
153 // to avoid </ sequence. Should add a flag to allow this
154 // compatibility mode and prevent this sequence from occurring.
155 default:
156 if (isControlCharacter(*c))
157 {
159 oss << "\\u" << std::hex << std::uppercase
160 << std::setfill('0') << std::setw(4)
161 << static_cast<int>(*c);
162 result += oss.str();
163 }
164 else
165 {
166 result += *c;
167 }
168
169 break;
170 }
171 }
172
173 result += "\"";
174 return result;
175}
176
177// Class FastWriter
178// //////////////////////////////////////////////////////////////////
179
182{
183 document_ = "";
184 writeValue(root);
185 return std::move(document_);
186}
187
188void
190{
191 switch (value.type())
192 {
193 case nullValue:
194 document_ += "null";
195 break;
196
197 case intValue:
198 document_ += valueToString(value.asInt());
199 break;
200
201 case uintValue:
202 document_ += valueToString(value.asUInt());
203 break;
204
205 case realValue:
206 document_ += valueToString(value.asDouble());
207 break;
208
209 case stringValue:
211 break;
212
213 case booleanValue:
214 document_ += valueToString(value.asBool());
215 break;
216
217 case arrayValue: {
218 document_ += "[";
219 int size = value.size();
220
221 for (int index = 0; index < size; ++index)
222 {
223 if (index > 0)
224 document_ += ",";
225
226 writeValue(value[index]);
227 }
228
229 document_ += "]";
230 }
231 break;
232
233 case objectValue: {
234 Value::Members members(value.getMemberNames());
235 document_ += "{";
236
237 for (Value::Members::iterator it = members.begin();
238 it != members.end();
239 ++it)
240 {
241 std::string const& name = *it;
242
243 if (it != members.begin())
244 document_ += ",";
245
247 document_ += ":";
248 writeValue(value[name]);
249 }
250
251 document_ += "}";
252 }
253 break;
254 }
255}
256
257// Class StyledWriter
258// //////////////////////////////////////////////////////////////////
259
260StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3)
261{
262}
263
266{
267 document_ = "";
268 addChildValues_ = false;
269 indentString_ = "";
270 writeValue(root);
271 document_ += "\n";
272 return document_;
273}
274
275void
277{
278 switch (value.type())
279 {
280 case nullValue:
281 pushValue("null");
282 break;
283
284 case intValue:
285 pushValue(valueToString(value.asInt()));
286 break;
287
288 case uintValue:
290 break;
291
292 case realValue:
294 break;
295
296 case stringValue:
298 break;
299
300 case booleanValue:
302 break;
303
304 case arrayValue:
305 writeArrayValue(value);
306 break;
307
308 case objectValue: {
309 Value::Members members(value.getMemberNames());
310
311 if (members.empty())
312 pushValue("{}");
313 else
314 {
315 writeWithIndent("{");
316 indent();
317 Value::Members::iterator it = members.begin();
318
319 while (true)
320 {
321 std::string const& name = *it;
322 Value const& childValue = value[name];
324 document_ += " : ";
325 writeValue(childValue);
326
327 if (++it == members.end())
328 break;
329
330 document_ += ",";
331 }
332
333 unindent();
334 writeWithIndent("}");
335 }
336 }
337 break;
338 }
339}
340
341void
343{
344 unsigned size = value.size();
345
346 if (size == 0)
347 pushValue("[]");
348 else
349 {
350 bool isArrayMultiLine = isMultineArray(value);
351
352 if (isArrayMultiLine)
353 {
354 writeWithIndent("[");
355 indent();
356 bool hasChildValue = !childValues_.empty();
357 unsigned index = 0;
358
359 while (true)
360 {
361 Value const& childValue = value[index];
362
363 if (hasChildValue)
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 size = value.size();
404 bool isMultiLine = size * 3 >= rightMargin_;
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()) &&
412 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 childValues_.push_back(value);
439 else
440 document_ += value;
441}
442
443void
445{
446 if (!document_.empty())
447 {
448 char last = document_[document_.length() - 1];
449
450 if (last == ' ') // already indented
451 return;
452
453 if (last != '\n') // Comments may add new-line
454 document_ += '\n';
455 }
456
458}
459
460void
462{
463 writeIndent();
464 document_ += value;
465}
466
467void
472
473void
475{
476 XRPL_ASSERT(
478 "Json::StyledWriter::unindent : maximum indent size");
480}
481
482// Class StyledStreamWriter
483// //////////////////////////////////////////////////////////////////
484
486 : document_(nullptr), rightMargin_(74), indentation_(indentation)
487{
488}
489
490void
492{
493 document_ = &out;
494 addChildValues_ = false;
495 indentString_ = "";
496 writeValue(root);
497 *document_ << "\n";
498 document_ = nullptr; // Forget the stream, for safety.
499}
500
501void
503{
504 switch (value.type())
505 {
506 case nullValue:
507 pushValue("null");
508 break;
509
510 case intValue:
511 pushValue(valueToString(value.asInt()));
512 break;
513
514 case uintValue:
516 break;
517
518 case realValue:
520 break;
521
522 case stringValue:
524 break;
525
526 case booleanValue:
528 break;
529
530 case arrayValue:
531 writeArrayValue(value);
532 break;
533
534 case objectValue: {
535 Value::Members members(value.getMemberNames());
536
537 if (members.empty())
538 pushValue("{}");
539 else
540 {
541 writeWithIndent("{");
542 indent();
543 Value::Members::iterator it = members.begin();
544
545 while (true)
546 {
547 std::string const& name = *it;
548 Value const& childValue = value[name];
550 *document_ << " : ";
551 writeValue(childValue);
552
553 if (++it == members.end())
554 break;
555
556 *document_ << ",";
557 }
558
559 unindent();
560 writeWithIndent("}");
561 }
562 }
563 break;
564 }
565}
566
567void
569{
570 unsigned size = value.size();
571
572 if (size == 0)
573 pushValue("[]");
574 else
575 {
576 bool isArrayMultiLine = isMultineArray(value);
577
578 if (isArrayMultiLine)
579 {
580 writeWithIndent("[");
581 indent();
582 bool hasChildValue = !childValues_.empty();
583 unsigned index = 0;
584
585 while (true)
586 {
587 Value const& childValue = value[index];
588
589 if (hasChildValue)
591 else
592 {
593 writeIndent();
594 writeValue(childValue);
595 }
596
597 if (++index == size)
598 break;
599
600 *document_ << ",";
601 }
602
603 unindent();
604 writeWithIndent("]");
605 }
606 else // output on a single line
607 {
608 XRPL_ASSERT(
609 childValues_.size() == size,
610 "Json::StyledStreamWriter::writeArrayValue : child size match");
611 *document_ << "[ ";
612
613 for (unsigned index = 0; index < size; ++index)
614 {
615 if (index > 0)
616 *document_ << ", ";
617
618 *document_ << childValues_[index];
619 }
620
621 *document_ << " ]";
622 }
623 }
624}
625
626bool
628{
629 int size = value.size();
630 bool isMultiLine = size * 3 >= rightMargin_;
632
633 for (int index = 0; index < size && !isMultiLine; ++index)
634 {
635 Value const& childValue = value[index];
636 isMultiLine = isMultiLine ||
637 ((childValue.isArray() || childValue.isObject()) &&
638 childValue.size() > 0);
639 }
640
641 if (!isMultiLine) // check if line length > max line length
642 {
643 childValues_.reserve(size);
644 addChildValues_ = true;
645 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
646
647 for (int index = 0; index < size; ++index)
648 {
649 writeValue(value[index]);
650 lineLength += int(childValues_[index].length());
651 }
652
653 addChildValues_ = false;
654 isMultiLine = isMultiLine || lineLength >= rightMargin_;
655 }
656
657 return isMultiLine;
658}
659
660void
662{
663 if (addChildValues_)
664 childValues_.push_back(value);
665 else
666 *document_ << value;
667}
668
669void
671{
672 /*
673 Some comments in this method would have been nice. ;-)
674
675 if ( !document_.empty() )
676 {
677 char last = document_[document_.length()-1];
678 if ( last == ' ' ) // already indented
679 return;
680 if ( last != '\n' ) // Comments may add new-line
681 *document_ << '\n';
682 }
683 */
684 *document_ << '\n' << indentString_;
685}
686
687void
689{
690 writeIndent();
691 *document_ << value;
692}
693
694void
699
700void
702{
703 XRPL_ASSERT(
705 "Json::StyledStreamWriter::unindent : maximum indent size");
707}
708
710operator<<(std::ostream& sout, Value const& root)
711{
713 writer.write(sout, root);
714 return sout;
715}
716
717} // 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)
bool isMultineArray(Value const &value)
void writeValue(Value const &value)
void writeArrayValue(Value const &value)
std::ostream * document_
StyledStreamWriter(std::string indentation="\t")
void pushValue(std::string const &value)
ChildValues childValues_
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)
bool isMultineArray(Value 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)