JsonCpp project page JsonCpp home page

json_writer.cpp
Go to the documentation of this file.
1#include <json/writer.h>
2#include <utility>
3#include <assert.h>
4#include <stdio.h>
5#include <string.h>
6#include <iostream>
7#include <sstream>
8#include <iomanip>
9
10#if _MSC_VER >= 1400 // VC++ 8.0
11#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
12#endif
13
14namespace Json {
15
16static bool isControlCharacter(char ch)
17{
18 return ch > 0 && ch <= 0x1F;
19}
20
21static bool containsControlCharacter( const char* str )
22{
23 while ( *str )
24 {
25 if ( isControlCharacter( *(str++) ) )
26 return true;
27 }
28 return false;
29}
30static void uintToString( unsigned int value,
31 char *&current )
32{
33 *--current = 0;
34 do
35 {
36 *--current = (value % 10) + '0';
37 value /= 10;
38 }
39 while ( value != 0 );
40}
41
42std::string valueToString( Int value )
43{
44 char buffer[32];
45 char *current = buffer + sizeof(buffer);
46 bool isNegative = value < 0;
47 if ( isNegative )
48 value = -value;
49 uintToString( UInt(value), current );
50 if ( isNegative )
51 *--current = '-';
52 assert( current >= buffer );
53 return current;
54}
55
56
57std::string valueToString( UInt value )
58{
59 char buffer[32];
60 char *current = buffer + sizeof(buffer);
61 uintToString( value, current );
62 assert( current >= buffer );
63 return current;
64}
65
66std::string valueToString( double value )
67{
68 char buffer[32];
69#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
70 sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
71#else
72 sprintf(buffer, "%#.16g", value);
73#endif
74 char* ch = buffer + strlen(buffer) - 1;
75 if (*ch != '0') return buffer; // nothing to truncate, so save time
76 while(ch > buffer && *ch == '0'){
77 --ch;
78 }
79 char* last_nonzero = ch;
80 while(ch >= buffer){
81 switch(*ch){
82 case '0':
83 case '1':
84 case '2':
85 case '3':
86 case '4':
87 case '5':
88 case '6':
89 case '7':
90 case '8':
91 case '9':
92 --ch;
93 continue;
94 case '.':
95 // Truncate zeroes to save bytes in output, but keep one.
96 *(last_nonzero+2) = '\0';
97 return buffer;
98 default:
99 return buffer;
100 }
101 }
102 return buffer;
103}
104
105
106std::string valueToString( bool value )
107{
108 return value ? "true" : "false";
109}
110
111std::string valueToQuotedString( const char *value )
112{
113 // Not sure how to handle unicode...
114 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
115 return std::string("\"") + value + "\"";
116 // We have to walk value and escape any special characters.
117 // Appending to std::string is not efficient, but this should be rare.
118 // (Note: forward slashes are *not* rare, but I am not escaping them.)
119 unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
120 std::string result;
121 result.reserve(maxsize); // to avoid lots of mallocs
122 result += "\"";
123 for (const char* c=value; *c != 0; ++c)
124 {
125 switch(*c)
126 {
127 case '\"':
128 result += "\\\"";
129 break;
130 case '\\':
131 result += "\\\\";
132 break;
133 case '\b':
134 result += "\\b";
135 break;
136 case '\f':
137 result += "\\f";
138 break;
139 case '\n':
140 result += "\\n";
141 break;
142 case '\r':
143 result += "\\r";
144 break;
145 case '\t':
146 result += "\\t";
147 break;
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 to avoid </
153 // sequence.
154 // Should add a flag to allow this compatibility mode and prevent this
155 // sequence from occurring.
156 default:
157 if ( isControlCharacter( *c ) )
158 {
159 std::ostringstream oss;
160 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
161 result += oss.str();
162 }
163 else
164 {
165 result += *c;
166 }
167 break;
168 }
169 }
170 result += "\"";
171 return result;
172}
173
174// Class Writer
175// //////////////////////////////////////////////////////////////////
177{
178}
179
180
181// Class FastWriter
182// //////////////////////////////////////////////////////////////////
183
185 : yamlCompatiblityEnabled_( false )
186{
187}
188
189
190void
192{
193 yamlCompatiblityEnabled_ = true;
194}
195
196
197std::string
199{
200 document_ = "";
201 writeValue( root );
202 document_ += "\n";
203 return document_;
204}
205
206
207void
208FastWriter::writeValue( const Value &value )
209{
210 switch ( value.type() )
211 {
212 case nullValue:
213 document_ += "null";
214 break;
215 case intValue:
216 document_ += valueToString( value.asInt() );
217 break;
218 case uintValue:
219 document_ += valueToString( value.asUInt() );
220 break;
221 case realValue:
222 document_ += valueToString( value.asDouble() );
223 break;
224 case stringValue:
225 document_ += valueToQuotedString( value.asCString() );
226 break;
227 case booleanValue:
228 document_ += valueToString( value.asBool() );
229 break;
230 case arrayValue:
231 {
232 document_ += "[";
233 int size = value.size();
234 for ( int index =0; index < size; ++index )
235 {
236 if ( index > 0 )
237 document_ += ",";
238 writeValue( value[index] );
239 }
240 document_ += "]";
241 }
242 break;
243 case objectValue:
244 {
245 Value::Members members( value.getMemberNames() );
246 document_ += "{";
247 for ( Value::Members::iterator it = members.begin();
248 it != members.end();
249 ++it )
250 {
251 const std::string &name = *it;
252 if ( it != members.begin() )
253 document_ += ",";
254 document_ += valueToQuotedString( name.c_str() );
255 document_ += yamlCompatiblityEnabled_ ? ": "
256 : ":";
257 writeValue( value[name] );
258 }
259 document_ += "}";
260 }
261 break;
262 }
263}
264
265
266// Class StyledWriter
267// //////////////////////////////////////////////////////////////////
268
270 : rightMargin_( 74 )
271 , indentSize_( 3 )
272{
273}
274
275
276std::string
278{
279 document_ = "";
280 addChildValues_ = false;
281 indentString_ = "";
282 writeCommentBeforeValue( root );
283 writeValue( root );
284 writeCommentAfterValueOnSameLine( root );
285 document_ += "\n";
286 return document_;
287}
288
289
290void
291StyledWriter::writeValue( const Value &value )
292{
293 switch ( value.type() )
294 {
295 case nullValue:
296 pushValue( "null" );
297 break;
298 case intValue:
299 pushValue( valueToString( value.asInt() ) );
300 break;
301 case uintValue:
302 pushValue( valueToString( value.asUInt() ) );
303 break;
304 case realValue:
305 pushValue( valueToString( value.asDouble() ) );
306 break;
307 case stringValue:
308 pushValue( valueToQuotedString( value.asCString() ) );
309 break;
310 case booleanValue:
311 pushValue( valueToString( value.asBool() ) );
312 break;
313 case arrayValue:
314 writeArrayValue( value);
315 break;
316 case objectValue:
317 {
318 Value::Members members( value.getMemberNames() );
319 if ( members.empty() )
320 pushValue( "{}" );
321 else
322 {
323 writeWithIndent( "{" );
324 indent();
325 Value::Members::iterator it = members.begin();
326 while ( true )
327 {
328 const std::string &name = *it;
329 const Value &childValue = value[name];
330 writeCommentBeforeValue( childValue );
331 writeWithIndent( valueToQuotedString( name.c_str() ) );
332 document_ += " : ";
333 writeValue( childValue );
334 if ( ++it == members.end() )
335 {
336 writeCommentAfterValueOnSameLine( childValue );
337 break;
338 }
339 document_ += ",";
340 writeCommentAfterValueOnSameLine( childValue );
341 }
342 unindent();
343 writeWithIndent( "}" );
344 }
345 }
346 break;
347 }
348}
349
350
351void
352StyledWriter::writeArrayValue( const Value &value )
353{
354 unsigned size = value.size();
355 if ( size == 0 )
356 pushValue( "[]" );
357 else
358 {
359 bool isArrayMultiLine = isMultineArray( value );
360 if ( isArrayMultiLine )
361 {
362 writeWithIndent( "[" );
363 indent();
364 bool hasChildValue = !childValues_.empty();
365 unsigned index =0;
366 while ( true )
367 {
368 const Value &childValue = value[index];
369 writeCommentBeforeValue( childValue );
370 if ( hasChildValue )
371 writeWithIndent( childValues_[index] );
372 else
373 {
374 writeIndent();
375 writeValue( childValue );
376 }
377 if ( ++index == size )
378 {
379 writeCommentAfterValueOnSameLine( childValue );
380 break;
381 }
382 document_ += ",";
383 writeCommentAfterValueOnSameLine( childValue );
384 }
385 unindent();
386 writeWithIndent( "]" );
387 }
388 else // output on a single line
389 {
390 assert( childValues_.size() == size );
391 document_ += "[ ";
392 for ( unsigned index =0; index < size; ++index )
393 {
394 if ( index > 0 )
395 document_ += ", ";
396 document_ += childValues_[index];
397 }
398 document_ += " ]";
399 }
400 }
401}
402
403
404bool
405StyledWriter::isMultineArray( const Value &value )
406{
407 int size = value.size();
408 bool isMultiLine = size*3 >= rightMargin_ ;
409 childValues_.clear();
410 for ( int index =0; index < size && !isMultiLine; ++index )
411 {
412 const Value &childValue = value[index];
413 isMultiLine = isMultiLine ||
414 ( (childValue.isArray() || childValue.isObject()) &&
415 childValue.size() > 0 );
416 }
417 if ( !isMultiLine ) // check if line length > max line length
418 {
419 childValues_.reserve( size );
420 addChildValues_ = true;
421 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
422 for ( int index =0; index < size && !isMultiLine; ++index )
423 {
424 writeValue( value[index] );
425 lineLength += int( childValues_[index].length() );
426 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
427 }
428 addChildValues_ = false;
429 isMultiLine = isMultiLine || lineLength >= rightMargin_;
430 }
431 return isMultiLine;
432}
433
434
435void
436StyledWriter::pushValue( const std::string &value )
437{
438 if ( addChildValues_ )
439 childValues_.push_back( value );
440 else
441 document_ += value;
442}
443
444
445void
446StyledWriter::writeIndent()
447{
448 if ( !document_.empty() )
449 {
450 char last = document_[document_.length()-1];
451 if ( last == ' ' ) // already indented
452 return;
453 if ( last != '\n' ) // Comments may add new-line
454 document_ += '\n';
455 }
456 document_ += indentString_;
457}
458
459
460void
461StyledWriter::writeWithIndent( const std::string &value )
462{
463 writeIndent();
464 document_ += value;
465}
466
467
468void
469StyledWriter::indent()
470{
471 indentString_ += std::string( indentSize_, ' ' );
472}
473
474
475void
476StyledWriter::unindent()
477{
478 assert( int(indentString_.size()) >= indentSize_ );
479 indentString_.resize( indentString_.size() - indentSize_ );
480}
481
482
483void
484StyledWriter::writeCommentBeforeValue( const Value &root )
485{
486 if ( !root.hasComment( commentBefore ) )
487 return;
488 document_ += normalizeEOL( root.getComment( commentBefore ) );
489 document_ += "\n";
490}
491
492
493void
494StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
495{
496 if ( root.hasComment( commentAfterOnSameLine ) )
497 document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
498
499 if ( root.hasComment( commentAfter ) )
500 {
501 document_ += "\n";
502 document_ += normalizeEOL( root.getComment( commentAfter ) );
503 document_ += "\n";
504 }
505}
506
507
508bool
509StyledWriter::hasCommentForValue( const Value &value )
510{
511 return value.hasComment( commentBefore )
512 || value.hasComment( commentAfterOnSameLine )
513 || value.hasComment( commentAfter );
514}
515
516
517std::string
518StyledWriter::normalizeEOL( const std::string &text )
519{
520 std::string normalized;
521 normalized.reserve( text.length() );
522 const char *begin = text.c_str();
523 const char *end = begin + text.length();
524 const char *current = begin;
525 while ( current != end )
526 {
527 char c = *current++;
528 if ( c == '\r' ) // mac or dos EOL
529 {
530 if ( *current == '\n' ) // convert dos EOL
531 ++current;
532 normalized += '\n';
533 }
534 else // handle unix EOL & other char
535 normalized += c;
536 }
537 return normalized;
538}
539
540
541// Class StyledStreamWriter
542// //////////////////////////////////////////////////////////////////
543
545 : document_(NULL)
546 , rightMargin_( 74 )
547 , indentation_( indentation )
548{
549}
550
551
552void
553StyledStreamWriter::write( std::ostream &out, const Value &root )
554{
555 document_ = &out;
556 addChildValues_ = false;
557 indentString_ = "";
558 writeCommentBeforeValue( root );
559 writeValue( root );
560 writeCommentAfterValueOnSameLine( root );
561 *document_ << "\n";
562 document_ = NULL; // Forget the stream, for safety.
563}
564
565
566void
567StyledStreamWriter::writeValue( const Value &value )
568{
569 switch ( value.type() )
570 {
571 case nullValue:
572 pushValue( "null" );
573 break;
574 case intValue:
575 pushValue( valueToString( value.asInt() ) );
576 break;
577 case uintValue:
578 pushValue( valueToString( value.asUInt() ) );
579 break;
580 case realValue:
581 pushValue( valueToString( value.asDouble() ) );
582 break;
583 case stringValue:
584 pushValue( valueToQuotedString( value.asCString() ) );
585 break;
586 case booleanValue:
587 pushValue( valueToString( value.asBool() ) );
588 break;
589 case arrayValue:
590 writeArrayValue( value);
591 break;
592 case objectValue:
593 {
594 Value::Members members( value.getMemberNames() );
595 if ( members.empty() )
596 pushValue( "{}" );
597 else
598 {
599 writeWithIndent( "{" );
600 indent();
601 Value::Members::iterator it = members.begin();
602 while ( true )
603 {
604 const std::string &name = *it;
605 const Value &childValue = value[name];
606 writeCommentBeforeValue( childValue );
607 writeWithIndent( valueToQuotedString( name.c_str() ) );
608 *document_ << " : ";
609 writeValue( childValue );
610 if ( ++it == members.end() )
611 {
612 writeCommentAfterValueOnSameLine( childValue );
613 break;
614 }
615 *document_ << ",";
616 writeCommentAfterValueOnSameLine( childValue );
617 }
618 unindent();
619 writeWithIndent( "}" );
620 }
621 }
622 break;
623 }
624}
625
626
627void
628StyledStreamWriter::writeArrayValue( const Value &value )
629{
630 unsigned size = value.size();
631 if ( size == 0 )
632 pushValue( "[]" );
633 else
634 {
635 bool isArrayMultiLine = isMultineArray( value );
636 if ( isArrayMultiLine )
637 {
638 writeWithIndent( "[" );
639 indent();
640 bool hasChildValue = !childValues_.empty();
641 unsigned index =0;
642 while ( true )
643 {
644 const Value &childValue = value[index];
645 writeCommentBeforeValue( childValue );
646 if ( hasChildValue )
647 writeWithIndent( childValues_[index] );
648 else
649 {
650 writeIndent();
651 writeValue( childValue );
652 }
653 if ( ++index == size )
654 {
655 writeCommentAfterValueOnSameLine( childValue );
656 break;
657 }
658 *document_ << ",";
659 writeCommentAfterValueOnSameLine( childValue );
660 }
661 unindent();
662 writeWithIndent( "]" );
663 }
664 else // output on a single line
665 {
666 assert( childValues_.size() == size );
667 *document_ << "[ ";
668 for ( unsigned index =0; index < size; ++index )
669 {
670 if ( index > 0 )
671 *document_ << ", ";
672 *document_ << childValues_[index];
673 }
674 *document_ << " ]";
675 }
676 }
677}
678
679
680bool
681StyledStreamWriter::isMultineArray( const Value &value )
682{
683 int size = value.size();
684 bool isMultiLine = size*3 >= rightMargin_ ;
685 childValues_.clear();
686 for ( int index =0; index < size && !isMultiLine; ++index )
687 {
688 const Value &childValue = value[index];
689 isMultiLine = isMultiLine ||
690 ( (childValue.isArray() || childValue.isObject()) &&
691 childValue.size() > 0 );
692 }
693 if ( !isMultiLine ) // check if line length > max line length
694 {
695 childValues_.reserve( size );
696 addChildValues_ = true;
697 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
698 for ( int index =0; index < size && !isMultiLine; ++index )
699 {
700 writeValue( value[index] );
701 lineLength += int( childValues_[index].length() );
702 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
703 }
704 addChildValues_ = false;
705 isMultiLine = isMultiLine || lineLength >= rightMargin_;
706 }
707 return isMultiLine;
708}
709
710
711void
712StyledStreamWriter::pushValue( const std::string &value )
713{
714 if ( addChildValues_ )
715 childValues_.push_back( value );
716 else
717 *document_ << value;
718}
719
720
721void
722StyledStreamWriter::writeIndent()
723{
724 /*
725 Some comments in this method would have been nice. ;-)
726
727 if ( !document_.empty() )
728 {
729 char last = document_[document_.length()-1];
730 if ( last == ' ' ) // already indented
731 return;
732 if ( last != '\n' ) // Comments may add new-line
733 *document_ << '\n';
734 }
735 */
736 *document_ << '\n' << indentString_;
737}
738
739
740void
741StyledStreamWriter::writeWithIndent( const std::string &value )
742{
743 writeIndent();
744 *document_ << value;
745}
746
747
748void
749StyledStreamWriter::indent()
750{
751 indentString_ += indentation_;
752}
753
754
755void
756StyledStreamWriter::unindent()
757{
758 assert( indentString_.size() >= indentation_.size() );
759 indentString_.resize( indentString_.size() - indentation_.size() );
760}
761
762
763void
764StyledStreamWriter::writeCommentBeforeValue( const Value &root )
765{
766 if ( !root.hasComment( commentBefore ) )
767 return;
768 *document_ << normalizeEOL( root.getComment( commentBefore ) );
769 *document_ << "\n";
770}
771
772
773void
774StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
775{
776 if ( root.hasComment( commentAfterOnSameLine ) )
777 *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
778
779 if ( root.hasComment( commentAfter ) )
780 {
781 *document_ << "\n";
782 *document_ << normalizeEOL( root.getComment( commentAfter ) );
783 *document_ << "\n";
784 }
785}
786
787
788bool
789StyledStreamWriter::hasCommentForValue( const Value &value )
790{
791 return value.hasComment( commentBefore )
792 || value.hasComment( commentAfterOnSameLine )
793 || value.hasComment( commentAfter );
794}
795
796
797std::string
798StyledStreamWriter::normalizeEOL( const std::string &text )
799{
800 std::string normalized;
801 normalized.reserve( text.length() );
802 const char *begin = text.c_str();
803 const char *end = begin + text.length();
804 const char *current = begin;
805 while ( current != end )
806 {
807 char c = *current++;
808 if ( c == '\r' ) // mac or dos EOL
809 {
810 if ( *current == '\n' ) // convert dos EOL
811 ++current;
812 normalized += '\n';
813 }
814 else // handle unix EOL & other char
815 normalized += c;
816 }
817 return normalized;
818}
819
820
821std::ostream& operator<<( std::ostream &sout, const Value &root )
822{
824 writer.write(sout, root);
825 return sout;
826}
827
828
829} // namespace Json
void enableYAMLCompatibility()
virtual std::string write(const Value &root)
Writes a Value in JSON format in a human friendly way, to a stream rather than to a string.
Definition: writer.h:123
void write(std::ostream &out, const Value &root)
Serialize a Value in JSON format.
StyledStreamWriter(std::string indentation="\t")
virtual std::string write(const Value &root)
Serialize a Value in JSON format.
Represents a JSON value.
Definition: value.h:112
const char * asCString() const
Definition: json_value.cpp:677
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:873
Int asInt() const
Definition: json_value.cpp:716
UInt asUInt() const
Definition: json_value.cpp:743
Members getMemberNames() const
Return a list of the member names.
ValueType type() const
Definition: json_value.cpp:528
bool asBool() const
Definition: json_value.cpp:795
std::vector< std::string > Members
Definition: value.h:119
double asDouble() const
Definition: json_value.cpp:770
virtual ~Writer()
JSON (JavaScript Object Notation).
Definition: features.h:6
static bool isControlCharacter(char ch)
Definition: json_writer.cpp:16
int Int
Definition: forwards.h:19
static void uintToString(unsigned int value, char *&current)
Definition: json_writer.cpp:30
@ commentAfterOnSameLine
a comment just after a value on the same line
Definition: value.h:38
@ commentBefore
a comment placed on the line before a value
Definition: value.h:37
@ commentAfter
a comment on the line after a value (only make sense for root value)
Definition: value.h:39
std::string valueToString(Int value)
Definition: json_writer.cpp:42
@ booleanValue
bool value
Definition: value.h:30
@ nullValue
'null' value
Definition: value.h:25
@ stringValue
UTF-8 string value.
Definition: value.h:29
@ realValue
double value
Definition: value.h:28
@ arrayValue
array value (ordered list)
Definition: value.h:31
@ intValue
signed integer value
Definition: value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition: value.h:32
@ uintValue
unsigned integer value
Definition: value.h:27
unsigned int UInt
Definition: forwards.h:20
std::ostream & operator<<(std::ostream &, const Value &root)
Output using the StyledStreamWriter.
std::string valueToQuotedString(const char *value)
static bool containsControlCharacter(const char *str)
Definition: json_writer.cpp:21

SourceForge Logo hosts this site. Send comments to:
Json-cpp Developers