rippled
Loading...
Searching...
No Matches
json_writer.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/beast/utility/instrumentation.h>
21#include <xrpl/json/json_forwards.h>
22#include <xrpl/json/json_value.h>
23#include <xrpl/json/json_writer.h>
24
25#include <cstdio>
26#include <cstring>
27#include <iomanip>
28#include <ios>
29#include <ostream>
30#include <sstream>
31#include <string>
32#include <utility>
33
34namespace Json {
35
36static bool
38{
39 return ch > 0 && ch <= 0x1F;
40}
41
42static bool
44{
45 while (*str)
46 {
47 if (isControlCharacter(*(str++)))
48 return true;
49 }
50
51 return false;
52}
53static void
54uintToString(unsigned int value, char*& current)
55{
56 *--current = 0;
57
58 do
59 {
60 *--current = (value % 10) + '0';
61 value /= 10;
62 } while (value != 0);
63}
64
67{
68 char buffer[32];
69 char* current = buffer + sizeof(buffer);
70 bool isNegative = value < 0;
71
72 if (isNegative)
73 value = -value;
74
75 uintToString(UInt(value), current);
76
77 if (isNegative)
78 *--current = '-';
79
80 XRPL_ASSERT(current >= buffer, "Json::valueToString(Int) : buffer check");
81 return current;
82}
83
86{
87 char buffer[32];
88 char* current = buffer + sizeof(buffer);
89 uintToString(value, current);
90 XRPL_ASSERT(current >= buffer, "Json::valueToString(UInt) : buffer check");
91 return current;
92}
93
95valueToString(double value)
96{
97 // Allocate a buffer that is more than large enough to store the 16 digits
98 // of precision requested below.
99 char buffer[32];
100 // Print into the buffer. We need not request the alternative representation
101 // that always has a decimal point because JSON doesn't distingish the
102 // concepts of reals and integers.
103#if defined(_MSC_VER) && \
104 defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005
105 // to avoid warning.
106 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
107#else
108 snprintf(buffer, sizeof(buffer), "%.16g", value);
109#endif
110 return buffer;
111}
112
114valueToString(bool value)
115{
116 return value ? "true" : "false";
117}
118
120valueToQuotedString(char const* value)
121{
122 // Not sure how to handle unicode...
123 if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr &&
125 return std::string("\"") + value + "\"";
126
127 // We have to walk value and escape any special characters.
128 // Appending to std::string is not efficient, but this should be rare.
129 // (Note: forward slashes are *not* rare, but I am not escaping them.)
130 unsigned maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL
131 std::string result;
132 result.reserve(maxsize); // to avoid lots of mallocs
133 result += "\"";
134
135 for (char const* c = value; *c != 0; ++c)
136 {
137 switch (*c)
138 {
139 case '\"':
140 result += "\\\"";
141 break;
142
143 case '\\':
144 result += "\\\\";
145 break;
146
147 case '\b':
148 result += "\\b";
149 break;
150
151 case '\f':
152 result += "\\f";
153 break;
154
155 case '\n':
156 result += "\\n";
157 break;
158
159 case '\r':
160 result += "\\r";
161 break;
162
163 case '\t':
164 result += "\\t";
165 break;
166
167 // case '/':
168 // Even though \/ is considered a legal escape in JSON, a bare
169 // slash is also legal, so I see no reason to escape it.
170 // (I hope I am not misunderstanding something.
171 // blep notes: actually escaping \/ may be useful in javascript
172 // to avoid </ sequence. Should add a flag to allow this
173 // compatibility mode and prevent this sequence from occurring.
174 default:
175 if (isControlCharacter(*c))
176 {
178 oss << "\\u" << std::hex << std::uppercase
179 << std::setfill('0') << std::setw(4)
180 << static_cast<int>(*c);
181 result += oss.str();
182 }
183 else
184 {
185 result += *c;
186 }
187
188 break;
189 }
190 }
191
192 result += "\"";
193 return result;
194}
195
196// Class FastWriter
197// //////////////////////////////////////////////////////////////////
198
201{
202 document_ = "";
203 writeValue(root);
204 return std::move(document_);
205}
206
207void
209{
210 switch (value.type())
211 {
212 case nullValue:
213 document_ += "null";
214 break;
215
216 case intValue:
217 document_ += valueToString(value.asInt());
218 break;
219
220 case uintValue:
221 document_ += valueToString(value.asUInt());
222 break;
223
224 case realValue:
225 document_ += valueToString(value.asDouble());
226 break;
227
228 case stringValue:
230 break;
231
232 case booleanValue:
233 document_ += valueToString(value.asBool());
234 break;
235
236 case arrayValue: {
237 document_ += "[";
238 int size = value.size();
239
240 for (int index = 0; index < size; ++index)
241 {
242 if (index > 0)
243 document_ += ",";
244
245 writeValue(value[index]);
246 }
247
248 document_ += "]";
249 }
250 break;
251
252 case objectValue: {
253 Value::Members members(value.getMemberNames());
254 document_ += "{";
255
256 for (Value::Members::iterator it = members.begin();
257 it != members.end();
258 ++it)
259 {
260 std::string const& name = *it;
261
262 if (it != members.begin())
263 document_ += ",";
264
266 document_ += ":";
267 writeValue(value[name]);
268 }
269
270 document_ += "}";
271 }
272 break;
273 }
274}
275
276// Class StyledWriter
277// //////////////////////////////////////////////////////////////////
278
279StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3)
280{
281}
282
285{
286 document_ = "";
287 addChildValues_ = false;
288 indentString_ = "";
289 writeValue(root);
290 document_ += "\n";
291 return document_;
292}
293
294void
296{
297 switch (value.type())
298 {
299 case nullValue:
300 pushValue("null");
301 break;
302
303 case intValue:
304 pushValue(valueToString(value.asInt()));
305 break;
306
307 case uintValue:
309 break;
310
311 case realValue:
313 break;
314
315 case stringValue:
317 break;
318
319 case booleanValue:
321 break;
322
323 case arrayValue:
324 writeArrayValue(value);
325 break;
326
327 case objectValue: {
328 Value::Members members(value.getMemberNames());
329
330 if (members.empty())
331 pushValue("{}");
332 else
333 {
334 writeWithIndent("{");
335 indent();
336 Value::Members::iterator it = members.begin();
337
338 while (true)
339 {
340 std::string const& name = *it;
341 Value const& childValue = value[name];
343 document_ += " : ";
344 writeValue(childValue);
345
346 if (++it == members.end())
347 break;
348
349 document_ += ",";
350 }
351
352 unindent();
353 writeWithIndent("}");
354 }
355 }
356 break;
357 }
358}
359
360void
362{
363 unsigned size = value.size();
364
365 if (size == 0)
366 pushValue("[]");
367 else
368 {
369 bool isArrayMultiLine = isMultineArray(value);
370
371 if (isArrayMultiLine)
372 {
373 writeWithIndent("[");
374 indent();
375 bool hasChildValue = !childValues_.empty();
376 unsigned index = 0;
377
378 while (true)
379 {
380 Value const& childValue = value[index];
381
382 if (hasChildValue)
384 else
385 {
386 writeIndent();
387 writeValue(childValue);
388 }
389
390 if (++index == size)
391 break;
392
393 document_ += ",";
394 }
395
396 unindent();
397 writeWithIndent("]");
398 }
399 else // output on a single line
400 {
401 XRPL_ASSERT(
402 childValues_.size() == size,
403 "Json::StyledWriter::writeArrayValue : child size match");
404 document_ += "[ ";
405
406 for (unsigned index = 0; index < size; ++index)
407 {
408 if (index > 0)
409 document_ += ", ";
410
411 document_ += childValues_[index];
412 }
413
414 document_ += " ]";
415 }
416 }
417}
418
419bool
421{
422 int size = value.size();
423 bool isMultiLine = size * 3 >= rightMargin_;
425
426 for (int index = 0; index < size && !isMultiLine; ++index)
427 {
428 Value const& childValue = value[index];
429 isMultiLine = isMultiLine ||
430 ((childValue.isArray() || childValue.isObject()) &&
431 childValue.size() > 0);
432 }
433
434 if (!isMultiLine) // check if line length > max line length
435 {
436 childValues_.reserve(size);
437 addChildValues_ = true;
438 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
439
440 for (int index = 0; index < size; ++index)
441 {
442 writeValue(value[index]);
443 lineLength += int(childValues_[index].length());
444 }
445
446 addChildValues_ = false;
447 isMultiLine = isMultiLine || lineLength >= rightMargin_;
448 }
449
450 return isMultiLine;
451}
452
453void
455{
456 if (addChildValues_)
457 childValues_.push_back(value);
458 else
459 document_ += value;
460}
461
462void
464{
465 if (!document_.empty())
466 {
467 char last = document_[document_.length() - 1];
468
469 if (last == ' ') // already indented
470 return;
471
472 if (last != '\n') // Comments may add new-line
473 document_ += '\n';
474 }
475
477}
478
479void
481{
482 writeIndent();
483 document_ += value;
484}
485
486void
491
492void
494{
495 XRPL_ASSERT(
497 "Json::StyledWriter::unindent : maximum indent size");
499}
500
501// Class StyledStreamWriter
502// //////////////////////////////////////////////////////////////////
503
505 : document_(nullptr), rightMargin_(74), indentation_(indentation)
506{
507}
508
509void
511{
512 document_ = &out;
513 addChildValues_ = false;
514 indentString_ = "";
515 writeValue(root);
516 *document_ << "\n";
517 document_ = nullptr; // Forget the stream, for safety.
518}
519
520void
522{
523 switch (value.type())
524 {
525 case nullValue:
526 pushValue("null");
527 break;
528
529 case intValue:
530 pushValue(valueToString(value.asInt()));
531 break;
532
533 case uintValue:
535 break;
536
537 case realValue:
539 break;
540
541 case stringValue:
543 break;
544
545 case booleanValue:
547 break;
548
549 case arrayValue:
550 writeArrayValue(value);
551 break;
552
553 case objectValue: {
554 Value::Members members(value.getMemberNames());
555
556 if (members.empty())
557 pushValue("{}");
558 else
559 {
560 writeWithIndent("{");
561 indent();
562 Value::Members::iterator it = members.begin();
563
564 while (true)
565 {
566 std::string const& name = *it;
567 Value const& childValue = value[name];
569 *document_ << " : ";
570 writeValue(childValue);
571
572 if (++it == members.end())
573 break;
574
575 *document_ << ",";
576 }
577
578 unindent();
579 writeWithIndent("}");
580 }
581 }
582 break;
583 }
584}
585
586void
588{
589 unsigned size = value.size();
590
591 if (size == 0)
592 pushValue("[]");
593 else
594 {
595 bool isArrayMultiLine = isMultineArray(value);
596
597 if (isArrayMultiLine)
598 {
599 writeWithIndent("[");
600 indent();
601 bool hasChildValue = !childValues_.empty();
602 unsigned index = 0;
603
604 while (true)
605 {
606 Value const& childValue = value[index];
607
608 if (hasChildValue)
610 else
611 {
612 writeIndent();
613 writeValue(childValue);
614 }
615
616 if (++index == size)
617 break;
618
619 *document_ << ",";
620 }
621
622 unindent();
623 writeWithIndent("]");
624 }
625 else // output on a single line
626 {
627 XRPL_ASSERT(
628 childValues_.size() == size,
629 "Json::StyledStreamWriter::writeArrayValue : child size match");
630 *document_ << "[ ";
631
632 for (unsigned index = 0; index < size; ++index)
633 {
634 if (index > 0)
635 *document_ << ", ";
636
637 *document_ << childValues_[index];
638 }
639
640 *document_ << " ]";
641 }
642 }
643}
644
645bool
647{
648 int size = value.size();
649 bool isMultiLine = size * 3 >= rightMargin_;
651
652 for (int index = 0; index < size && !isMultiLine; ++index)
653 {
654 Value const& childValue = value[index];
655 isMultiLine = isMultiLine ||
656 ((childValue.isArray() || childValue.isObject()) &&
657 childValue.size() > 0);
658 }
659
660 if (!isMultiLine) // check if line length > max line length
661 {
662 childValues_.reserve(size);
663 addChildValues_ = true;
664 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
665
666 for (int index = 0; index < size; ++index)
667 {
668 writeValue(value[index]);
669 lineLength += int(childValues_[index].length());
670 }
671
672 addChildValues_ = false;
673 isMultiLine = isMultiLine || lineLength >= rightMargin_;
674 }
675
676 return isMultiLine;
677}
678
679void
681{
682 if (addChildValues_)
683 childValues_.push_back(value);
684 else
685 *document_ << value;
686}
687
688void
690{
691 /*
692 Some comments in this method would have been nice. ;-)
693
694 if ( !document_.empty() )
695 {
696 char last = document_[document_.length()-1];
697 if ( last == ' ' ) // already indented
698 return;
699 if ( last != '\n' ) // Comments may add new-line
700 *document_ << '\n';
701 }
702 */
703 *document_ << '\n' << indentString_;
704}
705
706void
708{
709 writeIndent();
710 *document_ << value;
711}
712
713void
718
719void
721{
722 XRPL_ASSERT(
724 "Json::StyledStreamWriter::unindent : maximum indent size");
726}
727
729operator<<(std::ostream& sout, Value const& root)
730{
732 writer.write(sout, root);
733 return sout;
734}
735
736} // 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:69
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:149
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:25
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:43
@ nullValue
'null' value
Definition json_value.h:38
@ stringValue
UTF-8 string value.
Definition json_value.h:42
@ realValue
double value
Definition json_value.h:41
@ arrayValue
array value (ordered list)
Definition json_value.h:44
@ intValue
signed integer value
Definition json_value.h:39
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
@ uintValue
unsigned integer value
Definition json_value.h:40
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)