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