You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1264 lines
36 KiB

  1. // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
  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. #if !defined(JSON_IS_AMALGAMATION)
  6. #include "json_tool.h"
  7. #include <json/writer.h>
  8. #endif // if !defined(JSON_IS_AMALGAMATION)
  9. #include <algorithm>
  10. #include <cassert>
  11. #include <cctype>
  12. #include <cstring>
  13. #include <iomanip>
  14. #include <memory>
  15. #include <set>
  16. #include <sstream>
  17. #include <utility>
  18. #if __cplusplus >= 201103L
  19. #include <cmath>
  20. #include <cstdio>
  21. #if !defined(isnan)
  22. #define isnan std::isnan
  23. #endif
  24. #if !defined(isfinite)
  25. #define isfinite std::isfinite
  26. #endif
  27. #else
  28. #include <cmath>
  29. #include <cstdio>
  30. #if defined(_MSC_VER)
  31. #if !defined(isnan)
  32. #include <float.h>
  33. #define isnan _isnan
  34. #endif
  35. #if !defined(isfinite)
  36. #include <float.h>
  37. #define isfinite _finite
  38. #endif
  39. #if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
  40. #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
  41. #endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
  42. #endif //_MSC_VER
  43. #if defined(__sun) && defined(__SVR4) // Solaris
  44. #if !defined(isfinite)
  45. #include <ieeefp.h>
  46. #define isfinite finite
  47. #endif
  48. #endif
  49. #if defined(__hpux)
  50. #if !defined(isfinite)
  51. #if defined(__ia64) && !defined(finite)
  52. #define isfinite(x) \
  53. ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
  54. #endif
  55. #endif
  56. #endif
  57. #if !defined(isnan)
  58. // IEEE standard states that NaN values will not compare to themselves
  59. #define isnan(x) ((x) != (x))
  60. #endif
  61. #if !defined(__APPLE__)
  62. #if !defined(isfinite)
  63. #define isfinite finite
  64. #endif
  65. #endif
  66. #endif
  67. #if defined(_MSC_VER)
  68. // Disable warning about strdup being deprecated.
  69. #pragma warning(disable : 4996)
  70. #endif
  71. namespace Json {
  72. #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
  73. using StreamWriterPtr = std::unique_ptr<StreamWriter>;
  74. #else
  75. using StreamWriterPtr = std::auto_ptr<StreamWriter>;
  76. #endif
  77. String valueToString(LargestInt value) {
  78. UIntToStringBuffer buffer;
  79. char* current = buffer + sizeof(buffer);
  80. if (value == Value::minLargestInt) {
  81. uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
  82. *--current = '-';
  83. } else if (value < 0) {
  84. uintToString(LargestUInt(-value), current);
  85. *--current = '-';
  86. } else {
  87. uintToString(LargestUInt(value), current);
  88. }
  89. assert(current >= buffer);
  90. return current;
  91. }
  92. String valueToString(LargestUInt value) {
  93. UIntToStringBuffer buffer;
  94. char* current = buffer + sizeof(buffer);
  95. uintToString(value, current);
  96. assert(current >= buffer);
  97. return current;
  98. }
  99. #if defined(JSON_HAS_INT64)
  100. String valueToString(Int value) { return valueToString(LargestInt(value)); }
  101. String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
  102. #endif // # if defined(JSON_HAS_INT64)
  103. namespace {
  104. String valueToString(double value, bool useSpecialFloats,
  105. unsigned int precision, PrecisionType precisionType) {
  106. // Print into the buffer. We need not request the alternative representation
  107. // that always has a decimal point because JSON doesn't distinguish the
  108. // concepts of reals and integers.
  109. if (!isfinite(value)) {
  110. static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
  111. {"null", "-1e+9999", "1e+9999"}};
  112. return reps[useSpecialFloats ? 0 : 1][isnan(value) ? 0
  113. : (value < 0) ? 1
  114. : 2];
  115. }
  116. String buffer(size_t(36), '\0');
  117. while (true) {
  118. int len = jsoncpp_snprintf(
  119. &*buffer.begin(), buffer.size(),
  120. (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
  121. precision, value);
  122. assert(len >= 0);
  123. auto wouldPrint = static_cast<size_t>(len);
  124. if (wouldPrint >= buffer.size()) {
  125. buffer.resize(wouldPrint + 1);
  126. continue;
  127. }
  128. buffer.resize(wouldPrint);
  129. break;
  130. }
  131. buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
  132. // try to ensure we preserve the fact that this was given to us as a double on
  133. // input
  134. if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
  135. buffer += ".0";
  136. }
  137. // strip the zero padding from the right
  138. if (precisionType == PrecisionType::decimalPlaces) {
  139. buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
  140. buffer.end());
  141. }
  142. return buffer;
  143. }
  144. } // namespace
  145. String valueToString(double value, unsigned int precision,
  146. PrecisionType precisionType) {
  147. return valueToString(value, false, precision, precisionType);
  148. }
  149. String valueToString(bool value) { return value ? "true" : "false"; }
  150. static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
  151. assert(s || !n);
  152. return std::any_of(s, s + n, [](unsigned char c) {
  153. return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
  154. });
  155. }
  156. static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
  157. const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
  158. unsigned int firstByte = static_cast<unsigned char>(*s);
  159. if (firstByte < 0x80)
  160. return firstByte;
  161. if (firstByte < 0xE0) {
  162. if (e - s < 2)
  163. return REPLACEMENT_CHARACTER;
  164. unsigned int calculated =
  165. ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
  166. s += 1;
  167. // oversized encoded characters are invalid
  168. return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
  169. }
  170. if (firstByte < 0xF0) {
  171. if (e - s < 3)
  172. return REPLACEMENT_CHARACTER;
  173. unsigned int calculated = ((firstByte & 0x0F) << 12) |
  174. ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
  175. (static_cast<unsigned int>(s[2]) & 0x3F);
  176. s += 2;
  177. // surrogates aren't valid codepoints itself
  178. // shouldn't be UTF-8 encoded
  179. if (calculated >= 0xD800 && calculated <= 0xDFFF)
  180. return REPLACEMENT_CHARACTER;
  181. // oversized encoded characters are invalid
  182. return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
  183. }
  184. if (firstByte < 0xF8) {
  185. if (e - s < 4)
  186. return REPLACEMENT_CHARACTER;
  187. unsigned int calculated = ((firstByte & 0x07) << 18) |
  188. ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
  189. ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
  190. (static_cast<unsigned int>(s[3]) & 0x3F);
  191. s += 3;
  192. // oversized encoded characters are invalid
  193. return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
  194. }
  195. return REPLACEMENT_CHARACTER;
  196. }
  197. static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
  198. "101112131415161718191a1b1c1d1e1f"
  199. "202122232425262728292a2b2c2d2e2f"
  200. "303132333435363738393a3b3c3d3e3f"
  201. "404142434445464748494a4b4c4d4e4f"
  202. "505152535455565758595a5b5c5d5e5f"
  203. "606162636465666768696a6b6c6d6e6f"
  204. "707172737475767778797a7b7c7d7e7f"
  205. "808182838485868788898a8b8c8d8e8f"
  206. "909192939495969798999a9b9c9d9e9f"
  207. "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
  208. "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
  209. "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
  210. "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
  211. "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
  212. "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
  213. static String toHex16Bit(unsigned int x) {
  214. const unsigned int hi = (x >> 8) & 0xff;
  215. const unsigned int lo = x & 0xff;
  216. String result(4, ' ');
  217. result[0] = hex2[2 * hi];
  218. result[1] = hex2[2 * hi + 1];
  219. result[2] = hex2[2 * lo];
  220. result[3] = hex2[2 * lo + 1];
  221. return result;
  222. }
  223. static void appendRaw(String& result, unsigned ch) {
  224. result += static_cast<char>(ch);
  225. }
  226. static void appendHex(String& result, unsigned ch) {
  227. result.append("\\u").append(toHex16Bit(ch));
  228. }
  229. static String valueToQuotedStringN(const char* value, size_t length,
  230. bool emitUTF8 = false) {
  231. if (value == nullptr)
  232. return "";
  233. if (!doesAnyCharRequireEscaping(value, length))
  234. return String("\"") + value + "\"";
  235. // We have to walk value and escape any special characters.
  236. // Appending to String is not efficient, but this should be rare.
  237. // (Note: forward slashes are *not* rare, but I am not escaping them.)
  238. String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
  239. String result;
  240. result.reserve(maxsize); // to avoid lots of mallocs
  241. result += "\"";
  242. char const* end = value + length;
  243. for (const char* c = value; c != end; ++c) {
  244. switch (*c) {
  245. case '\"':
  246. result += "\\\"";
  247. break;
  248. case '\\':
  249. result += "\\\\";
  250. break;
  251. case '\b':
  252. result += "\\b";
  253. break;
  254. case '\f':
  255. result += "\\f";
  256. break;
  257. case '\n':
  258. result += "\\n";
  259. break;
  260. case '\r':
  261. result += "\\r";
  262. break;
  263. case '\t':
  264. result += "\\t";
  265. break;
  266. // case '/':
  267. // Even though \/ is considered a legal escape in JSON, a bare
  268. // slash is also legal, so I see no reason to escape it.
  269. // (I hope I am not misunderstanding something.)
  270. // blep notes: actually escaping \/ may be useful in javascript to avoid </
  271. // sequence.
  272. // Should add a flag to allow this compatibility mode and prevent this
  273. // sequence from occurring.
  274. default: {
  275. if (emitUTF8) {
  276. unsigned codepoint = static_cast<unsigned char>(*c);
  277. if (codepoint < 0x20) {
  278. appendHex(result, codepoint);
  279. } else {
  280. appendRaw(result, codepoint);
  281. }
  282. } else {
  283. unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
  284. if (codepoint < 0x20) {
  285. appendHex(result, codepoint);
  286. } else if (codepoint < 0x80) {
  287. appendRaw(result, codepoint);
  288. } else if (codepoint < 0x10000) {
  289. // Basic Multilingual Plane
  290. appendHex(result, codepoint);
  291. } else {
  292. // Extended Unicode. Encode 20 bits as a surrogate pair.
  293. codepoint -= 0x10000;
  294. appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
  295. appendHex(result, 0xdc00 + (codepoint & 0x3ff));
  296. }
  297. }
  298. } break;
  299. }
  300. }
  301. result += "\"";
  302. return result;
  303. }
  304. String valueToQuotedString(const char* value) {
  305. return valueToQuotedStringN(value, strlen(value));
  306. }
  307. String valueToQuotedString(const char* value, size_t length) {
  308. return valueToQuotedStringN(value, length);
  309. }
  310. // Class Writer
  311. // //////////////////////////////////////////////////////////////////
  312. Writer::~Writer() = default;
  313. // Class FastWriter
  314. // //////////////////////////////////////////////////////////////////
  315. FastWriter::FastWriter()
  316. = default;
  317. void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
  318. void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
  319. void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
  320. String FastWriter::write(const Value& root) {
  321. document_.clear();
  322. writeValue(root);
  323. if (!omitEndingLineFeed_)
  324. document_ += '\n';
  325. return document_;
  326. }
  327. void FastWriter::writeValue(const Value& value) {
  328. switch (value.type()) {
  329. case nullValue:
  330. if (!dropNullPlaceholders_)
  331. document_ += "null";
  332. break;
  333. case intValue:
  334. document_ += valueToString(value.asLargestInt());
  335. break;
  336. case uintValue:
  337. document_ += valueToString(value.asLargestUInt());
  338. break;
  339. case realValue:
  340. document_ += valueToString(value.asDouble());
  341. break;
  342. case stringValue: {
  343. // Is NULL possible for value.string_? No.
  344. char const* str;
  345. char const* end;
  346. bool ok = value.getString(&str, &end);
  347. if (ok)
  348. document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
  349. break;
  350. }
  351. case booleanValue:
  352. document_ += valueToString(value.asBool());
  353. break;
  354. case arrayValue: {
  355. document_ += '[';
  356. ArrayIndex size = value.size();
  357. for (ArrayIndex index = 0; index < size; ++index) {
  358. if (index > 0)
  359. document_ += ',';
  360. writeValue(value[index]);
  361. }
  362. document_ += ']';
  363. } break;
  364. case objectValue: {
  365. Value::Members members(value.getMemberNames());
  366. document_ += '{';
  367. for (auto it = members.begin(); it != members.end(); ++it) {
  368. const String& name = *it;
  369. if (it != members.begin())
  370. document_ += ',';
  371. document_ += valueToQuotedStringN(name.data(), name.length());
  372. document_ += yamlCompatibilityEnabled_ ? ": " : ":";
  373. writeValue(value[name]);
  374. }
  375. document_ += '}';
  376. } break;
  377. }
  378. }
  379. // Class StyledWriter
  380. // //////////////////////////////////////////////////////////////////
  381. StyledWriter::StyledWriter() = default;
  382. String StyledWriter::write(const Value& root) {
  383. document_.clear();
  384. addChildValues_ = false;
  385. indentString_.clear();
  386. writeCommentBeforeValue(root);
  387. writeValue(root);
  388. writeCommentAfterValueOnSameLine(root);
  389. document_ += '\n';
  390. return document_;
  391. }
  392. void StyledWriter::writeValue(const Value& value) {
  393. switch (value.type()) {
  394. case nullValue:
  395. pushValue("null");
  396. break;
  397. case intValue:
  398. pushValue(valueToString(value.asLargestInt()));
  399. break;
  400. case uintValue:
  401. pushValue(valueToString(value.asLargestUInt()));
  402. break;
  403. case realValue:
  404. pushValue(valueToString(value.asDouble()));
  405. break;
  406. case stringValue: {
  407. // Is NULL possible for value.string_? No.
  408. char const* str;
  409. char const* end;
  410. bool ok = value.getString(&str, &end);
  411. if (ok)
  412. pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
  413. else
  414. pushValue("");
  415. break;
  416. }
  417. case booleanValue:
  418. pushValue(valueToString(value.asBool()));
  419. break;
  420. case arrayValue:
  421. writeArrayValue(value);
  422. break;
  423. case objectValue: {
  424. Value::Members members(value.getMemberNames());
  425. if (members.empty())
  426. pushValue("{}");
  427. else {
  428. writeWithIndent("{");
  429. indent();
  430. auto it = members.begin();
  431. for (;;) {
  432. const String& name = *it;
  433. const Value& childValue = value[name];
  434. writeCommentBeforeValue(childValue);
  435. writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
  436. document_ += " : ";
  437. writeValue(childValue);
  438. if (++it == members.end()) {
  439. writeCommentAfterValueOnSameLine(childValue);
  440. break;
  441. }
  442. document_ += ',';
  443. writeCommentAfterValueOnSameLine(childValue);
  444. }
  445. unindent();
  446. writeWithIndent("}");
  447. }
  448. } break;
  449. }
  450. }
  451. void StyledWriter::writeArrayValue(const Value& value) {
  452. size_t size = value.size();
  453. if (size == 0)
  454. pushValue("[]");
  455. else {
  456. bool isArrayMultiLine = isMultilineArray(value);
  457. if (isArrayMultiLine) {
  458. writeWithIndent("[");
  459. indent();
  460. bool hasChildValue = !childValues_.empty();
  461. ArrayIndex index = 0;
  462. for (;;) {
  463. const Value& childValue = value[index];
  464. writeCommentBeforeValue(childValue);
  465. if (hasChildValue)
  466. writeWithIndent(childValues_[index]);
  467. else {
  468. writeIndent();
  469. writeValue(childValue);
  470. }
  471. if (++index == size) {
  472. writeCommentAfterValueOnSameLine(childValue);
  473. break;
  474. }
  475. document_ += ',';
  476. writeCommentAfterValueOnSameLine(childValue);
  477. }
  478. unindent();
  479. writeWithIndent("]");
  480. } else // output on a single line
  481. {
  482. assert(childValues_.size() == size);
  483. document_ += "[ ";
  484. for (size_t index = 0; index < size; ++index) {
  485. if (index > 0)
  486. document_ += ", ";
  487. document_ += childValues_[index];
  488. }
  489. document_ += " ]";
  490. }
  491. }
  492. }
  493. bool StyledWriter::isMultilineArray(const Value& value) {
  494. ArrayIndex const size = value.size();
  495. bool isMultiLine = size * 3 >= rightMargin_;
  496. childValues_.clear();
  497. for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
  498. const Value& childValue = value[index];
  499. isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
  500. !childValue.empty());
  501. }
  502. if (!isMultiLine) // check if line length > max line length
  503. {
  504. childValues_.reserve(size);
  505. addChildValues_ = true;
  506. ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
  507. for (ArrayIndex index = 0; index < size; ++index) {
  508. if (hasCommentForValue(value[index])) {
  509. isMultiLine = true;
  510. }
  511. writeValue(value[index]);
  512. lineLength += static_cast<ArrayIndex>(childValues_[index].length());
  513. }
  514. addChildValues_ = false;
  515. isMultiLine = isMultiLine || lineLength >= rightMargin_;
  516. }
  517. return isMultiLine;
  518. }
  519. void StyledWriter::pushValue(const String& value) {
  520. if (addChildValues_)
  521. childValues_.push_back(value);
  522. else
  523. document_ += value;
  524. }
  525. void StyledWriter::writeIndent() {
  526. if (!document_.empty()) {
  527. char last = document_[document_.length() - 1];
  528. if (last == ' ') // already indented
  529. return;
  530. if (last != '\n') // Comments may add new-line
  531. document_ += '\n';
  532. }
  533. document_ += indentString_;
  534. }
  535. void StyledWriter::writeWithIndent(const String& value) {
  536. writeIndent();
  537. document_ += value;
  538. }
  539. void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
  540. void StyledWriter::unindent() {
  541. assert(indentString_.size() >= indentSize_);
  542. indentString_.resize(indentString_.size() - indentSize_);
  543. }
  544. void StyledWriter::writeCommentBeforeValue(const Value& root) {
  545. if (!root.hasComment(commentBefore))
  546. return;
  547. document_ += '\n';
  548. writeIndent();
  549. const String& comment = root.getComment(commentBefore);
  550. String::const_iterator iter = comment.begin();
  551. while (iter != comment.end()) {
  552. document_ += *iter;
  553. if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
  554. writeIndent();
  555. ++iter;
  556. }
  557. // Comments are stripped of trailing newlines, so add one here
  558. document_ += '\n';
  559. }
  560. void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
  561. if (root.hasComment(commentAfterOnSameLine))
  562. document_ += " " + root.getComment(commentAfterOnSameLine);
  563. if (root.hasComment(commentAfter)) {
  564. document_ += '\n';
  565. document_ += root.getComment(commentAfter);
  566. document_ += '\n';
  567. }
  568. }
  569. bool StyledWriter::hasCommentForValue(const Value& value) {
  570. return value.hasComment(commentBefore) ||
  571. value.hasComment(commentAfterOnSameLine) ||
  572. value.hasComment(commentAfter);
  573. }
  574. // Class StyledStreamWriter
  575. // //////////////////////////////////////////////////////////////////
  576. StyledStreamWriter::StyledStreamWriter(String indentation)
  577. : document_(nullptr), indentation_(std::move(indentation)),
  578. addChildValues_(), indented_(false) {}
  579. void StyledStreamWriter::write(OStream& out, const Value& root) {
  580. document_ = &out;
  581. addChildValues_ = false;
  582. indentString_.clear();
  583. indented_ = true;
  584. writeCommentBeforeValue(root);
  585. if (!indented_)
  586. writeIndent();
  587. indented_ = true;
  588. writeValue(root);
  589. writeCommentAfterValueOnSameLine(root);
  590. *document_ << "\n";
  591. document_ = nullptr; // Forget the stream, for safety.
  592. }
  593. void StyledStreamWriter::writeValue(const Value& value) {
  594. switch (value.type()) {
  595. case nullValue:
  596. pushValue("null");
  597. break;
  598. case intValue:
  599. pushValue(valueToString(value.asLargestInt()));
  600. break;
  601. case uintValue:
  602. pushValue(valueToString(value.asLargestUInt()));
  603. break;
  604. case realValue:
  605. pushValue(valueToString(value.asDouble()));
  606. break;
  607. case stringValue: {
  608. // Is NULL possible for value.string_? No.
  609. char const* str;
  610. char const* end;
  611. bool ok = value.getString(&str, &end);
  612. if (ok)
  613. pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
  614. else
  615. pushValue("");
  616. break;
  617. }
  618. case booleanValue:
  619. pushValue(valueToString(value.asBool()));
  620. break;
  621. case arrayValue:
  622. writeArrayValue(value);
  623. break;
  624. case objectValue: {
  625. Value::Members members(value.getMemberNames());
  626. if (members.empty())
  627. pushValue("{}");
  628. else {
  629. writeWithIndent("{");
  630. indent();
  631. auto it = members.begin();
  632. for (;;) {
  633. const String& name = *it;
  634. const Value& childValue = value[name];
  635. writeCommentBeforeValue(childValue);
  636. writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
  637. *document_ << " : ";
  638. writeValue(childValue);
  639. if (++it == members.end()) {
  640. writeCommentAfterValueOnSameLine(childValue);
  641. break;
  642. }
  643. *document_ << ",";
  644. writeCommentAfterValueOnSameLine(childValue);
  645. }
  646. unindent();
  647. writeWithIndent("}");
  648. }
  649. } break;
  650. }
  651. }
  652. void StyledStreamWriter::writeArrayValue(const Value& value) {
  653. unsigned size = value.size();
  654. if (size == 0)
  655. pushValue("[]");
  656. else {
  657. bool isArrayMultiLine = isMultilineArray(value);
  658. if (isArrayMultiLine) {
  659. writeWithIndent("[");
  660. indent();
  661. bool hasChildValue = !childValues_.empty();
  662. unsigned index = 0;
  663. for (;;) {
  664. const Value& childValue = value[index];
  665. writeCommentBeforeValue(childValue);
  666. if (hasChildValue)
  667. writeWithIndent(childValues_[index]);
  668. else {
  669. if (!indented_)
  670. writeIndent();
  671. indented_ = true;
  672. writeValue(childValue);
  673. indented_ = false;
  674. }
  675. if (++index == size) {
  676. writeCommentAfterValueOnSameLine(childValue);
  677. break;
  678. }
  679. *document_ << ",";
  680. writeCommentAfterValueOnSameLine(childValue);
  681. }
  682. unindent();
  683. writeWithIndent("]");
  684. } else // output on a single line
  685. {
  686. assert(childValues_.size() == size);
  687. *document_ << "[ ";
  688. for (unsigned index = 0; index < size; ++index) {
  689. if (index > 0)
  690. *document_ << ", ";
  691. *document_ << childValues_[index];
  692. }
  693. *document_ << " ]";
  694. }
  695. }
  696. }
  697. bool StyledStreamWriter::isMultilineArray(const Value& value) {
  698. ArrayIndex const size = value.size();
  699. bool isMultiLine = size * 3 >= rightMargin_;
  700. childValues_.clear();
  701. for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
  702. const Value& childValue = value[index];
  703. isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
  704. !childValue.empty());
  705. }
  706. if (!isMultiLine) // check if line length > max line length
  707. {
  708. childValues_.reserve(size);
  709. addChildValues_ = true;
  710. ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
  711. for (ArrayIndex index = 0; index < size; ++index) {
  712. if (hasCommentForValue(value[index])) {
  713. isMultiLine = true;
  714. }
  715. writeValue(value[index]);
  716. lineLength += static_cast<ArrayIndex>(childValues_[index].length());
  717. }
  718. addChildValues_ = false;
  719. isMultiLine = isMultiLine || lineLength >= rightMargin_;
  720. }
  721. return isMultiLine;
  722. }
  723. void StyledStreamWriter::pushValue(const String& value) {
  724. if (addChildValues_)
  725. childValues_.push_back(value);
  726. else
  727. *document_ << value;
  728. }
  729. void StyledStreamWriter::writeIndent() {
  730. // blep intended this to look at the so-far-written string
  731. // to determine whether we are already indented, but
  732. // with a stream we cannot do that. So we rely on some saved state.
  733. // The caller checks indented_.
  734. *document_ << '\n' << indentString_;
  735. }
  736. void StyledStreamWriter::writeWithIndent(const String& value) {
  737. if (!indented_)
  738. writeIndent();
  739. *document_ << value;
  740. indented_ = false;
  741. }
  742. void StyledStreamWriter::indent() { indentString_ += indentation_; }
  743. void StyledStreamWriter::unindent() {
  744. assert(indentString_.size() >= indentation_.size());
  745. indentString_.resize(indentString_.size() - indentation_.size());
  746. }
  747. void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
  748. if (!root.hasComment(commentBefore))
  749. return;
  750. if (!indented_)
  751. writeIndent();
  752. const String& comment = root.getComment(commentBefore);
  753. String::const_iterator iter = comment.begin();
  754. while (iter != comment.end()) {
  755. *document_ << *iter;
  756. if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
  757. // writeIndent(); // would include newline
  758. *document_ << indentString_;
  759. ++iter;
  760. }
  761. indented_ = false;
  762. }
  763. void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
  764. if (root.hasComment(commentAfterOnSameLine))
  765. *document_ << ' ' << root.getComment(commentAfterOnSameLine);
  766. if (root.hasComment(commentAfter)) {
  767. writeIndent();
  768. *document_ << root.getComment(commentAfter);
  769. }
  770. indented_ = false;
  771. }
  772. bool StyledStreamWriter::hasCommentForValue(const Value& value) {
  773. return value.hasComment(commentBefore) ||
  774. value.hasComment(commentAfterOnSameLine) ||
  775. value.hasComment(commentAfter);
  776. }
  777. //////////////////////////
  778. // BuiltStyledStreamWriter
  779. /// Scoped enums are not available until C++11.
  780. struct CommentStyle {
  781. /// Decide whether to write comments.
  782. enum Enum {
  783. None, ///< Drop all comments.
  784. Most, ///< Recover odd behavior of previous versions (not implemented yet).
  785. All ///< Keep all comments.
  786. };
  787. };
  788. struct BuiltStyledStreamWriter : public StreamWriter {
  789. BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
  790. String colonSymbol, String nullSymbol,
  791. String endingLineFeedSymbol, bool useSpecialFloats,
  792. bool emitUTF8, unsigned int precision,
  793. PrecisionType precisionType);
  794. int write(Value const& root, OStream* sout) override;
  795. private:
  796. void writeValue(Value const& value);
  797. void writeArrayValue(Value const& value);
  798. bool isMultilineArray(Value const& value);
  799. void pushValue(String const& value);
  800. void writeIndent();
  801. void writeWithIndent(String const& value);
  802. void indent();
  803. void unindent();
  804. void writeCommentBeforeValue(Value const& root);
  805. void writeCommentAfterValueOnSameLine(Value const& root);
  806. static bool hasCommentForValue(const Value& value);
  807. using ChildValues = std::vector<String>;
  808. ChildValues childValues_;
  809. String indentString_;
  810. unsigned int rightMargin_;
  811. String indentation_;
  812. CommentStyle::Enum cs_;
  813. String colonSymbol_;
  814. String nullSymbol_;
  815. String endingLineFeedSymbol_;
  816. bool addChildValues_ : 1;
  817. bool indented_ : 1;
  818. bool useSpecialFloats_ : 1;
  819. bool emitUTF8_ : 1;
  820. unsigned int precision_;
  821. PrecisionType precisionType_;
  822. };
  823. BuiltStyledStreamWriter::BuiltStyledStreamWriter(
  824. String indentation, CommentStyle::Enum cs, String colonSymbol,
  825. String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
  826. bool emitUTF8, unsigned int precision, PrecisionType precisionType)
  827. : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
  828. colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
  829. endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
  830. addChildValues_(false), indented_(false),
  831. useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
  832. precision_(precision), precisionType_(precisionType) {}
  833. int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
  834. sout_ = sout;
  835. addChildValues_ = false;
  836. indented_ = true;
  837. indentString_.clear();
  838. writeCommentBeforeValue(root);
  839. if (!indented_)
  840. writeIndent();
  841. indented_ = true;
  842. writeValue(root);
  843. writeCommentAfterValueOnSameLine(root);
  844. *sout_ << endingLineFeedSymbol_;
  845. sout_ = nullptr;
  846. return 0;
  847. }
  848. void BuiltStyledStreamWriter::writeValue(Value const& value) {
  849. switch (value.type()) {
  850. case nullValue:
  851. pushValue(nullSymbol_);
  852. break;
  853. case intValue:
  854. pushValue(valueToString(value.asLargestInt()));
  855. break;
  856. case uintValue:
  857. pushValue(valueToString(value.asLargestUInt()));
  858. break;
  859. case realValue:
  860. pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
  861. precisionType_));
  862. break;
  863. case stringValue: {
  864. // Is NULL is possible for value.string_? No.
  865. char const* str;
  866. char const* end;
  867. bool ok = value.getString(&str, &end);
  868. if (ok)
  869. pushValue(
  870. valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
  871. else
  872. pushValue("");
  873. break;
  874. }
  875. case booleanValue:
  876. pushValue(valueToString(value.asBool()));
  877. break;
  878. case arrayValue:
  879. writeArrayValue(value);
  880. break;
  881. case objectValue: {
  882. Value::Members members(value.getMemberNames());
  883. if (members.empty())
  884. pushValue("{}");
  885. else {
  886. writeWithIndent("{");
  887. indent();
  888. auto it = members.begin();
  889. for (;;) {
  890. String const& name = *it;
  891. Value const& childValue = value[name];
  892. writeCommentBeforeValue(childValue);
  893. writeWithIndent(
  894. valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
  895. *sout_ << colonSymbol_;
  896. writeValue(childValue);
  897. if (++it == members.end()) {
  898. writeCommentAfterValueOnSameLine(childValue);
  899. break;
  900. }
  901. *sout_ << ",";
  902. writeCommentAfterValueOnSameLine(childValue);
  903. }
  904. unindent();
  905. writeWithIndent("}");
  906. }
  907. } break;
  908. }
  909. }
  910. void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
  911. unsigned size = value.size();
  912. if (size == 0)
  913. pushValue("[]");
  914. else {
  915. bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
  916. if (isMultiLine) {
  917. writeWithIndent("[");
  918. indent();
  919. bool hasChildValue = !childValues_.empty();
  920. unsigned index = 0;
  921. for (;;) {
  922. Value const& childValue = value[index];
  923. writeCommentBeforeValue(childValue);
  924. if (hasChildValue)
  925. writeWithIndent(childValues_[index]);
  926. else {
  927. if (!indented_)
  928. writeIndent();
  929. indented_ = true;
  930. writeValue(childValue);
  931. indented_ = false;
  932. }
  933. if (++index == size) {
  934. writeCommentAfterValueOnSameLine(childValue);
  935. break;
  936. }
  937. *sout_ << ",";
  938. writeCommentAfterValueOnSameLine(childValue);
  939. }
  940. unindent();
  941. writeWithIndent("]");
  942. } else // output on a single line
  943. {
  944. assert(childValues_.size() == size);
  945. *sout_ << "[";
  946. if (!indentation_.empty())
  947. *sout_ << " ";
  948. for (unsigned index = 0; index < size; ++index) {
  949. if (index > 0)
  950. *sout_ << ((!indentation_.empty()) ? ", " : ",");
  951. *sout_ << childValues_[index];
  952. }
  953. if (!indentation_.empty())
  954. *sout_ << " ";
  955. *sout_ << "]";
  956. }
  957. }
  958. }
  959. bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
  960. ArrayIndex const size = value.size();
  961. bool isMultiLine = size * 3 >= rightMargin_;
  962. childValues_.clear();
  963. for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
  964. Value const& childValue = value[index];
  965. isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
  966. !childValue.empty());
  967. }
  968. if (!isMultiLine) // check if line length > max line length
  969. {
  970. childValues_.reserve(size);
  971. addChildValues_ = true;
  972. ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
  973. for (ArrayIndex index = 0; index < size; ++index) {
  974. if (hasCommentForValue(value[index])) {
  975. isMultiLine = true;
  976. }
  977. writeValue(value[index]);
  978. lineLength += static_cast<ArrayIndex>(childValues_[index].length());
  979. }
  980. addChildValues_ = false;
  981. isMultiLine = isMultiLine || lineLength >= rightMargin_;
  982. }
  983. return isMultiLine;
  984. }
  985. void BuiltStyledStreamWriter::pushValue(String const& value) {
  986. if (addChildValues_)
  987. childValues_.push_back(value);
  988. else
  989. *sout_ << value;
  990. }
  991. void BuiltStyledStreamWriter::writeIndent() {
  992. // blep intended this to look at the so-far-written string
  993. // to determine whether we are already indented, but
  994. // with a stream we cannot do that. So we rely on some saved state.
  995. // The caller checks indented_.
  996. if (!indentation_.empty()) {
  997. // In this case, drop newlines too.
  998. *sout_ << '\n' << indentString_;
  999. }
  1000. }
  1001. void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
  1002. if (!indented_)
  1003. writeIndent();
  1004. *sout_ << value;
  1005. indented_ = false;
  1006. }
  1007. void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
  1008. void BuiltStyledStreamWriter::unindent() {
  1009. assert(indentString_.size() >= indentation_.size());
  1010. indentString_.resize(indentString_.size() - indentation_.size());
  1011. }
  1012. void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
  1013. if (cs_ == CommentStyle::None)
  1014. return;
  1015. if (!root.hasComment(commentBefore))
  1016. return;
  1017. if (!indented_)
  1018. writeIndent();
  1019. const String& comment = root.getComment(commentBefore);
  1020. String::const_iterator iter = comment.begin();
  1021. while (iter != comment.end()) {
  1022. *sout_ << *iter;
  1023. if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
  1024. // writeIndent(); // would write extra newline
  1025. *sout_ << indentString_;
  1026. ++iter;
  1027. }
  1028. indented_ = false;
  1029. }
  1030. void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
  1031. Value const& root) {
  1032. if (cs_ == CommentStyle::None)
  1033. return;
  1034. if (root.hasComment(commentAfterOnSameLine))
  1035. *sout_ << " " + root.getComment(commentAfterOnSameLine);
  1036. if (root.hasComment(commentAfter)) {
  1037. writeIndent();
  1038. *sout_ << root.getComment(commentAfter);
  1039. }
  1040. }
  1041. // static
  1042. bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
  1043. return value.hasComment(commentBefore) ||
  1044. value.hasComment(commentAfterOnSameLine) ||
  1045. value.hasComment(commentAfter);
  1046. }
  1047. ///////////////
  1048. // StreamWriter
  1049. StreamWriter::StreamWriter() : sout_(nullptr) {}
  1050. StreamWriter::~StreamWriter() = default;
  1051. StreamWriter::Factory::~Factory() = default;
  1052. StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
  1053. StreamWriterBuilder::~StreamWriterBuilder() = default;
  1054. StreamWriter* StreamWriterBuilder::newStreamWriter() const {
  1055. const String indentation = settings_["indentation"].asString();
  1056. const String cs_str = settings_["commentStyle"].asString();
  1057. const String pt_str = settings_["precisionType"].asString();
  1058. const bool eyc = settings_["enableYAMLCompatibility"].asBool();
  1059. const bool dnp = settings_["dropNullPlaceholders"].asBool();
  1060. const bool usf = settings_["useSpecialFloats"].asBool();
  1061. const bool emitUTF8 = settings_["emitUTF8"].asBool();
  1062. unsigned int pre = settings_["precision"].asUInt();
  1063. CommentStyle::Enum cs = CommentStyle::All;
  1064. if (cs_str == "All") {
  1065. cs = CommentStyle::All;
  1066. } else if (cs_str == "None") {
  1067. cs = CommentStyle::None;
  1068. } else {
  1069. throwRuntimeError("commentStyle must be 'All' or 'None'");
  1070. }
  1071. PrecisionType precisionType(significantDigits);
  1072. if (pt_str == "significant") {
  1073. precisionType = PrecisionType::significantDigits;
  1074. } else if (pt_str == "decimal") {
  1075. precisionType = PrecisionType::decimalPlaces;
  1076. } else {
  1077. throwRuntimeError("precisionType must be 'significant' or 'decimal'");
  1078. }
  1079. String colonSymbol = " : ";
  1080. if (eyc) {
  1081. colonSymbol = ": ";
  1082. } else if (indentation.empty()) {
  1083. colonSymbol = ":";
  1084. }
  1085. String nullSymbol = "null";
  1086. if (dnp) {
  1087. nullSymbol.clear();
  1088. }
  1089. if (pre > 17)
  1090. pre = 17;
  1091. String endingLineFeedSymbol;
  1092. return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
  1093. endingLineFeedSymbol, usf, emitUTF8, pre,
  1094. precisionType);
  1095. }
  1096. bool StreamWriterBuilder::validate(Json::Value* invalid) const {
  1097. static const auto& valid_keys = *new std::set<String>{
  1098. "indentation",
  1099. "commentStyle",
  1100. "enableYAMLCompatibility",
  1101. "dropNullPlaceholders",
  1102. "useSpecialFloats",
  1103. "emitUTF8",
  1104. "precision",
  1105. "precisionType",
  1106. };
  1107. for (auto si = settings_.begin(); si != settings_.end(); ++si) {
  1108. auto key = si.name();
  1109. if (valid_keys.count(key))
  1110. continue;
  1111. if (invalid)
  1112. (*invalid)[key] = *si;
  1113. else
  1114. return false;
  1115. }
  1116. return invalid ? invalid->empty() : true;
  1117. }
  1118. Value& StreamWriterBuilder::operator[](const String& key) {
  1119. return settings_[key];
  1120. }
  1121. // static
  1122. void StreamWriterBuilder::setDefaults(Json::Value* settings) {
  1123. //! [StreamWriterBuilderDefaults]
  1124. (*settings)["commentStyle"] = "All";
  1125. (*settings)["indentation"] = "\t";
  1126. (*settings)["enableYAMLCompatibility"] = false;
  1127. (*settings)["dropNullPlaceholders"] = false;
  1128. (*settings)["useSpecialFloats"] = false;
  1129. (*settings)["emitUTF8"] = false;
  1130. (*settings)["precision"] = 17;
  1131. (*settings)["precisionType"] = "significant";
  1132. //! [StreamWriterBuilderDefaults]
  1133. }
  1134. String writeString(StreamWriter::Factory const& factory, Value const& root) {
  1135. OStringStream sout;
  1136. StreamWriterPtr const writer(factory.newStreamWriter());
  1137. writer->write(root, &sout);
  1138. return std::move(sout).str();
  1139. }
  1140. OStream& operator<<(OStream& sout, Value const& root) {
  1141. StreamWriterBuilder builder;
  1142. StreamWriterPtr const writer(builder.newStreamWriter());
  1143. writer->write(root, &sout);
  1144. return sout;
  1145. }
  1146. } // namespace Json