json_writer.cpp 20 KB

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