comparison third_party/json/json_writer.cpp @ 0:add35537fdbb tip

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