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