1 // Copyright 2007-2010 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
6 #include "json_writer.h"
16 #if _MSC_VER >= 1400 // VC++ 8.0
17 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
22 static bool containsControlCharacter( const char* str )
26 if ( isControlCharacter( *(str++) ) )
32 std::string valueToString( Int value )
34 UIntToStringBuffer buffer;
35 char *current = buffer + sizeof(buffer);
36 bool isNegative = value < 0;
39 uintToString( UInt(value), current );
42 assert( current >= buffer );
47 std::string valueToString( UInt value )
49 UIntToStringBuffer buffer;
50 char *current = buffer + sizeof(buffer);
51 uintToString( value, current );
52 assert( current >= buffer );
56 std::string valueToString( double value )
59 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
60 sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
62 sprintf(buffer, "%#.16g", value);
64 char* ch = buffer + strlen(buffer) - 1;
65 if (*ch != '0') return buffer; // nothing to truncate, so save time
66 while(ch > buffer && *ch == '0'){
69 char* last_nonzero = ch;
85 // Truncate zeroes to save bytes in output, but keep one.
86 *(last_nonzero+2) = '\0';
96 std::string valueToString( bool value )
98 return value ? "true" : "false";
101 std::string valueToQuotedString( const char *value )
103 // Not sure how to handle unicode...
104 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
105 return std::string("\"") + value + "\"";
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 std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
111 result.reserve(maxsize); // to avoid lots of mallocs
113 for (const char* c=value; *c != 0; ++c)
139 // Even though \/ is considered a legal escape in JSON, a bare
140 // slash is also legal, so I see no reason to escape it.
141 // (I hope I am not misunderstanding something.
142 // blep notes: actually escaping \/ may be useful in javascript to avoid </
144 // Should add a flag to allow this compatibility mode and prevent this
145 // sequence from occurring.
147 if ( isControlCharacter( *c ) )
149 std::ostringstream oss;
150 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
165 // //////////////////////////////////////////////////////////////////
172 // //////////////////////////////////////////////////////////////////
174 FastWriter::FastWriter()
175 : yamlCompatiblityEnabled_( false )
181 FastWriter::enableYAMLCompatibility()
183 yamlCompatiblityEnabled_ = true;
188 FastWriter::write( const Value &root )
198 FastWriter::writeValue( const Value &value )
200 switch ( value.type() )
206 document_ += valueToString( value.asInt() );
209 document_ += valueToString( value.asUInt() );
212 document_ += valueToString( value.asDouble() );
215 document_ += valueToQuotedString( value.asCString() );
218 document_ += valueToString( value.asBool() );
223 int size = value.size();
224 for ( int index =0; index < size; ++index )
228 writeValue( value[index] );
235 Value::Members members( value.getMemberNames() );
237 for ( Value::Members::iterator it = members.begin();
241 const std::string &name = *it;
242 if ( it != members.begin() )
244 document_ += valueToQuotedString( name.c_str() );
245 document_ += yamlCompatiblityEnabled_ ? ": "
247 writeValue( value[name] );
256 // Class StyledWriter
257 // //////////////////////////////////////////////////////////////////
259 StyledWriter::StyledWriter()
267 StyledWriter::write( const Value &root )
270 addChildValues_ = false;
272 writeCommentBeforeValue( root );
274 writeCommentAfterValueOnSameLine( root );
281 StyledWriter::writeValue( const Value &value )
283 switch ( value.type() )
289 pushValue( valueToString( value.asInt() ) );
292 pushValue( valueToString( value.asUInt() ) );
295 pushValue( valueToString( value.asDouble() ) );
298 pushValue( valueToQuotedString( value.asCString() ) );
301 pushValue( valueToString( value.asBool() ) );
304 writeArrayValue( value);
308 Value::Members members( value.getMemberNames() );
309 if ( members.empty() )
313 writeWithIndent( "{" );
315 Value::Members::iterator it = members.begin();
318 const std::string &name = *it;
319 const Value &childValue = value[name];
320 writeCommentBeforeValue( childValue );
321 writeWithIndent( valueToQuotedString( name.c_str() ) );
323 writeValue( childValue );
324 if ( ++it == members.end() )
326 writeCommentAfterValueOnSameLine( childValue );
330 writeCommentAfterValueOnSameLine( childValue );
333 writeWithIndent( "}" );
342 StyledWriter::writeArrayValue( const Value &value )
344 unsigned size = value.size();
349 bool isArrayMultiLine = isMultineArray( value );
350 if ( isArrayMultiLine )
352 writeWithIndent( "[" );
354 bool hasChildValue = !childValues_.empty();
358 const Value &childValue = value[index];
359 writeCommentBeforeValue( childValue );
361 writeWithIndent( childValues_[index] );
365 writeValue( childValue );
367 if ( ++index == size )
369 writeCommentAfterValueOnSameLine( childValue );
373 writeCommentAfterValueOnSameLine( childValue );
376 writeWithIndent( "]" );
378 else // output on a single line
380 assert( childValues_.size() == size );
382 for ( unsigned index =0; index < size; ++index )
386 document_ += childValues_[index];
395 StyledWriter::isMultineArray( const Value &value )
397 int size = value.size();
398 bool isMultiLine = size*3 >= rightMargin_ ;
399 childValues_.clear();
400 for ( int index =0; index < size && !isMultiLine; ++index )
402 const Value &childValue = value[index];
403 isMultiLine = isMultiLine ||
404 ( (childValue.isArray() || childValue.isObject()) &&
405 childValue.size() > 0 );
407 if ( !isMultiLine ) // check if line length > max line length
409 childValues_.reserve( size );
410 addChildValues_ = true;
411 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
412 for ( int index =0; index < size && !isMultiLine; ++index )
414 writeValue( value[index] );
415 lineLength += int( childValues_[index].length() );
416 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
418 addChildValues_ = false;
419 isMultiLine = isMultiLine || lineLength >= rightMargin_;
426 StyledWriter::pushValue( const std::string &value )
428 if ( addChildValues_ )
429 childValues_.push_back( value );
436 StyledWriter::writeIndent()
438 if ( !document_.empty() )
440 char last = document_[document_.length()-1];
441 if ( last == ' ' ) // already indented
443 if ( last != '\n' ) // Comments may add new-line
446 document_ += indentString_;
451 StyledWriter::writeWithIndent( const std::string &value )
459 StyledWriter::indent()
461 indentString_ += std::string( indentSize_, ' ' );
466 StyledWriter::unindent()
468 assert( int(indentString_.size()) >= indentSize_ );
469 indentString_.resize( indentString_.size() - indentSize_ );
474 StyledWriter::writeCommentBeforeValue( const Value &root )
476 if ( !root.hasComment( commentBefore ) )
478 document_ += normalizeEOL( root.getComment( commentBefore ) );
484 StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
486 if ( root.hasComment( commentAfterOnSameLine ) )
487 document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
489 if ( root.hasComment( commentAfter ) )
492 document_ += normalizeEOL( root.getComment( commentAfter ) );
499 StyledWriter::hasCommentForValue( const Value &value )
501 return value.hasComment( commentBefore )
502 || value.hasComment( commentAfterOnSameLine )
503 || value.hasComment( commentAfter );
508 StyledWriter::normalizeEOL( const std::string &text )
510 std::string normalized;
511 normalized.reserve( text.length() );
512 const char *begin = text.c_str();
513 const char *end = begin + text.length();
514 const char *current = begin;
515 while ( current != end )
518 if ( c == '\r' ) // mac or dos EOL
520 if ( *current == '\n' ) // convert dos EOL
524 else // handle unix EOL & other char
531 // Class StyledStreamWriter
532 // //////////////////////////////////////////////////////////////////
534 StyledStreamWriter::StyledStreamWriter( std::string indentation )
537 , indentation_( indentation )
543 StyledStreamWriter::write( std::ostream &out, const Value &root )
546 addChildValues_ = false;
548 writeCommentBeforeValue( root );
550 writeCommentAfterValueOnSameLine( root );
552 document_ = NULL; // Forget the stream, for safety.
557 StyledStreamWriter::writeValue( const Value &value )
559 switch ( value.type() )
565 pushValue( valueToString( value.asInt() ) );
568 pushValue( valueToString( value.asUInt() ) );
571 pushValue( valueToString( value.asDouble() ) );
574 pushValue( valueToQuotedString( value.asCString() ) );
577 pushValue( valueToString( value.asBool() ) );
580 writeArrayValue( value);
584 Value::Members members( value.getMemberNames() );
585 if ( members.empty() )
589 writeWithIndent( "{" );
591 Value::Members::iterator it = members.begin();
594 const std::string &name = *it;
595 const Value &childValue = value[name];
596 writeCommentBeforeValue( childValue );
597 writeWithIndent( valueToQuotedString( name.c_str() ) );
599 writeValue( childValue );
600 if ( ++it == members.end() )
602 writeCommentAfterValueOnSameLine( childValue );
606 writeCommentAfterValueOnSameLine( childValue );
609 writeWithIndent( "}" );
618 StyledStreamWriter::writeArrayValue( const Value &value )
620 unsigned size = value.size();
625 bool isArrayMultiLine = isMultineArray( value );
626 if ( isArrayMultiLine )
628 writeWithIndent( "[" );
630 bool hasChildValue = !childValues_.empty();
634 const Value &childValue = value[index];
635 writeCommentBeforeValue( childValue );
637 writeWithIndent( childValues_[index] );
641 writeValue( childValue );
643 if ( ++index == size )
645 writeCommentAfterValueOnSameLine( childValue );
649 writeCommentAfterValueOnSameLine( childValue );
652 writeWithIndent( "]" );
654 else // output on a single line
656 assert( childValues_.size() == size );
658 for ( unsigned index =0; index < size; ++index )
662 *document_ << childValues_[index];
671 StyledStreamWriter::isMultineArray( const Value &value )
673 int size = value.size();
674 bool isMultiLine = size*3 >= rightMargin_ ;
675 childValues_.clear();
676 for ( int index =0; index < size && !isMultiLine; ++index )
678 const Value &childValue = value[index];
679 isMultiLine = isMultiLine ||
680 ( (childValue.isArray() || childValue.isObject()) &&
681 childValue.size() > 0 );
683 if ( !isMultiLine ) // check if line length > max line length
685 childValues_.reserve( size );
686 addChildValues_ = true;
687 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
688 for ( int index =0; index < size && !isMultiLine; ++index )
690 writeValue( value[index] );
691 lineLength += int( childValues_[index].length() );
692 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
694 addChildValues_ = false;
695 isMultiLine = isMultiLine || lineLength >= rightMargin_;
702 StyledStreamWriter::pushValue( const std::string &value )
704 if ( addChildValues_ )
705 childValues_.push_back( value );
712 StyledStreamWriter::writeIndent()
715 Some comments in this method would have been nice. ;-)
717 if ( !document_.empty() )
719 char last = document_[document_.length()-1];
720 if ( last == ' ' ) // already indented
722 if ( last != '\n' ) // Comments may add new-line
726 *document_ << '\n' << indentString_;
731 StyledStreamWriter::writeWithIndent( const std::string &value )
739 StyledStreamWriter::indent()
741 indentString_ += indentation_;
746 StyledStreamWriter::unindent()
748 assert( indentString_.size() >= indentation_.size() );
749 indentString_.resize( indentString_.size() - indentation_.size() );
754 StyledStreamWriter::writeCommentBeforeValue( const Value &root )
756 if ( !root.hasComment( commentBefore ) )
758 *document_ << normalizeEOL( root.getComment( commentBefore ) );
764 StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
766 if ( root.hasComment( commentAfterOnSameLine ) )
767 *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
769 if ( root.hasComment( commentAfter ) )
772 *document_ << normalizeEOL( root.getComment( commentAfter ) );
779 StyledStreamWriter::hasCommentForValue( const Value &value )
781 return value.hasComment( commentBefore )
782 || value.hasComment( commentAfterOnSameLine )
783 || value.hasComment( commentAfter );
788 StyledStreamWriter::normalizeEOL( const std::string &text )
790 std::string normalized;
791 normalized.reserve( text.length() );
792 const char *begin = text.c_str();
793 const char *end = begin + text.length();
794 const char *current = begin;
795 while ( current != end )
798 if ( c == '\r' ) // mac or dos EOL
800 if ( *current == '\n' ) // convert dos EOL
804 else // handle unix EOL & other char
811 std::ostream& operator<<( std::ostream &sout, const Value &root )
813 Json::StyledStreamWriter writer;
814 writer.write(sout, root);