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
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include <json/writer.h>
9 #endif // if !defined(JSON_IS_AMALGAMATION)
21 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
23 #define isfinite _finite
24 #define snprintf _snprintf
27 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
28 // Disable warning about strdup being deprecated.
29 #pragma warning(disable : 4996)
32 #if defined(__sun) && defined(__SVR4) //Solaris
34 #define isfinite finite
39 #if __cplusplus >= 201103L
40 typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
42 typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
45 static bool containsControlCharacter(const char* str) {
47 if (isControlCharacter(*(str++)))
53 std::string valueToString(LargestInt value) {
54 UIntToStringBuffer buffer;
55 char* current = buffer + sizeof(buffer);
56 bool isNegative = value < 0;
59 uintToString(LargestUInt(value), current);
62 assert(current >= buffer);
66 std::string valueToString(LargestUInt value) {
67 UIntToStringBuffer buffer;
68 char* current = buffer + sizeof(buffer);
69 uintToString(value, current);
70 assert(current >= buffer);
74 #if defined(JSON_HAS_INT64)
76 std::string valueToString(Int value) {
77 return valueToString(LargestInt(value));
80 std::string valueToString(UInt value) {
81 return valueToString(LargestUInt(value));
84 #endif // # if defined(JSON_HAS_INT64)
86 std::string valueToString(double value) {
87 // Allocate a buffer that is more than large enough to store the 16 digits of
88 // precision requested below.
92 // Print into the buffer. We need not request the alternative representation
93 // that always has a decimal point because JSON doesn't distingish the
94 // concepts of reals and integers.
95 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
96 // visual studio 2005 to
99 len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
101 len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
104 if (isfinite(value)) {
105 len = snprintf(buffer, sizeof(buffer), "%.17g", value);
107 // IEEE standard states that NaN values will not compare to themselves
108 if (value != value) {
109 len = snprintf(buffer, sizeof(buffer), "null");
110 } else if (value < 0) {
111 len = snprintf(buffer, sizeof(buffer), "-1e+9999");
113 len = snprintf(buffer, sizeof(buffer), "1e+9999");
115 // For those, we do not need to call fixNumLoc, but it is fast.
119 fixNumericLocale(buffer, buffer + len);
123 std::string valueToString(bool value) { return value ? "true" : "false"; }
125 std::string valueToQuotedString(const char* value) {
128 // Not sure how to handle unicode...
129 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
130 !containsControlCharacter(value))
131 return std::string("\"") + value + "\"";
132 // We have to walk value and escape any special characters.
133 // Appending to std::string is not efficient, but this should be rare.
134 // (Note: forward slashes are *not* rare, but I am not escaping them.)
135 std::string::size_type maxsize =
136 strlen(value) * 2 + 3; // allescaped+quotes+NULL
138 result.reserve(maxsize); // to avoid lots of mallocs
140 for (const char* c = value; *c != 0; ++c) {
164 // Even though \/ is considered a legal escape in JSON, a bare
165 // slash is also legal, so I see no reason to escape it.
166 // (I hope I am not misunderstanding something.
167 // blep notes: actually escaping \/ may be useful in javascript to avoid </
169 // Should add a flag to allow this compatibility mode and prevent this
170 // sequence from occurring.
172 if (isControlCharacter(*c)) {
173 std::ostringstream oss;
174 oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
175 << std::setw(4) << static_cast<int>(*c);
188 // //////////////////////////////////////////////////////////////////
192 // //////////////////////////////////////////////////////////////////
194 FastWriter::FastWriter()
195 : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
196 omitEndingLineFeed_(false) {}
198 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
200 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
202 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
204 std::string FastWriter::write(const Value& root) {
207 if (!omitEndingLineFeed_)
212 void FastWriter::writeValue(const Value& value) {
213 switch (value.type()) {
215 if (!dropNullPlaceholders_)
219 document_ += valueToString(value.asLargestInt());
222 document_ += valueToString(value.asLargestUInt());
225 document_ += valueToString(value.asDouble());
228 document_ += valueToQuotedString(value.asCString());
231 document_ += valueToString(value.asBool());
235 int size = value.size();
236 for (int index = 0; index < size; ++index) {
239 writeValue(value[index]);
244 Value::Members members(value.getMemberNames());
246 for (Value::Members::iterator it = members.begin(); it != members.end();
248 const std::string& name = *it;
249 if (it != members.begin())
251 document_ += valueToQuotedString(name.c_str());
252 document_ += yamlCompatiblityEnabled_ ? ": " : ":";
253 writeValue(value[name]);
260 // Class StyledWriter
261 // //////////////////////////////////////////////////////////////////
263 StyledWriter::StyledWriter()
264 : rightMargin_(74), indentSize_(3), addChildValues_() {}
266 std::string StyledWriter::write(const Value& root) {
268 addChildValues_ = false;
270 writeCommentBeforeValue(root);
272 writeCommentAfterValueOnSameLine(root);
277 void StyledWriter::writeValue(const Value& value) {
278 switch (value.type()) {
283 pushValue(valueToString(value.asLargestInt()));
286 pushValue(valueToString(value.asLargestUInt()));
289 pushValue(valueToString(value.asDouble()));
292 pushValue(valueToQuotedString(value.asCString()));
295 pushValue(valueToString(value.asBool()));
298 writeArrayValue(value);
301 Value::Members members(value.getMemberNames());
305 writeWithIndent("{");
307 Value::Members::iterator it = members.begin();
309 const std::string& name = *it;
310 const Value& childValue = value[name];
311 writeCommentBeforeValue(childValue);
312 writeWithIndent(valueToQuotedString(name.c_str()));
314 writeValue(childValue);
315 if (++it == members.end()) {
316 writeCommentAfterValueOnSameLine(childValue);
320 writeCommentAfterValueOnSameLine(childValue);
323 writeWithIndent("}");
329 void StyledWriter::writeArrayValue(const Value& value) {
330 unsigned size = value.size();
334 bool isArrayMultiLine = isMultineArray(value);
335 if (isArrayMultiLine) {
336 writeWithIndent("[");
338 bool hasChildValue = !childValues_.empty();
341 const Value& childValue = value[index];
342 writeCommentBeforeValue(childValue);
344 writeWithIndent(childValues_[index]);
347 writeValue(childValue);
349 if (++index == size) {
350 writeCommentAfterValueOnSameLine(childValue);
354 writeCommentAfterValueOnSameLine(childValue);
357 writeWithIndent("]");
358 } else // output on a single line
360 assert(childValues_.size() == size);
362 for (unsigned index = 0; index < size; ++index) {
365 document_ += childValues_[index];
372 bool StyledWriter::isMultineArray(const Value& value) {
373 int size = value.size();
374 bool isMultiLine = size * 3 >= rightMargin_;
375 childValues_.clear();
376 for (int index = 0; index < size && !isMultiLine; ++index) {
377 const Value& childValue = value[index];
379 isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
380 childValue.size() > 0);
382 if (!isMultiLine) // check if line length > max line length
384 childValues_.reserve(size);
385 addChildValues_ = true;
386 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
387 for (int index = 0; index < size; ++index) {
388 if (hasCommentForValue(value[index])) {
391 writeValue(value[index]);
392 lineLength += int(childValues_[index].length());
394 addChildValues_ = false;
395 isMultiLine = isMultiLine || lineLength >= rightMargin_;
400 void StyledWriter::pushValue(const std::string& value) {
402 childValues_.push_back(value);
407 void StyledWriter::writeIndent() {
408 if (!document_.empty()) {
409 char last = document_[document_.length() - 1];
410 if (last == ' ') // already indented
412 if (last != '\n') // Comments may add new-line
415 document_ += indentString_;
418 void StyledWriter::writeWithIndent(const std::string& value) {
423 void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
425 void StyledWriter::unindent() {
426 assert(int(indentString_.size()) >= indentSize_);
427 indentString_.resize(indentString_.size() - indentSize_);
430 void StyledWriter::writeCommentBeforeValue(const Value& root) {
431 if (!root.hasComment(commentBefore))
436 const std::string& comment = root.getComment(commentBefore);
437 std::string::const_iterator iter = comment.begin();
438 while (iter != comment.end()) {
441 (iter != comment.end() && *(iter + 1) == '/'))
446 // Comments are stripped of trailing newlines, so add one here
450 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
451 if (root.hasComment(commentAfterOnSameLine))
452 document_ += " " + root.getComment(commentAfterOnSameLine);
454 if (root.hasComment(commentAfter)) {
456 document_ += root.getComment(commentAfter);
461 bool StyledWriter::hasCommentForValue(const Value& value) {
462 return value.hasComment(commentBefore) ||
463 value.hasComment(commentAfterOnSameLine) ||
464 value.hasComment(commentAfter);
467 // Class StyledStreamWriter
468 // //////////////////////////////////////////////////////////////////
470 StyledStreamWriter::StyledStreamWriter(std::string indentation)
471 : document_(NULL), rightMargin_(74), indentation_(indentation),
474 void StyledStreamWriter::write(std::ostream& out, const Value& root) {
476 addChildValues_ = false;
479 writeCommentBeforeValue(root);
480 if (!indented_) writeIndent();
483 writeCommentAfterValueOnSameLine(root);
485 document_ = NULL; // Forget the stream, for safety.
488 void StyledStreamWriter::writeValue(const Value& value) {
489 switch (value.type()) {
494 pushValue(valueToString(value.asLargestInt()));
497 pushValue(valueToString(value.asLargestUInt()));
500 pushValue(valueToString(value.asDouble()));
503 pushValue(valueToQuotedString(value.asCString()));
506 pushValue(valueToString(value.asBool()));
509 writeArrayValue(value);
512 Value::Members members(value.getMemberNames());
516 writeWithIndent("{");
518 Value::Members::iterator it = members.begin();
520 const std::string& name = *it;
521 const Value& childValue = value[name];
522 writeCommentBeforeValue(childValue);
523 writeWithIndent(valueToQuotedString(name.c_str()));
525 writeValue(childValue);
526 if (++it == members.end()) {
527 writeCommentAfterValueOnSameLine(childValue);
531 writeCommentAfterValueOnSameLine(childValue);
534 writeWithIndent("}");
540 void StyledStreamWriter::writeArrayValue(const Value& value) {
541 unsigned size = value.size();
545 bool isArrayMultiLine = isMultineArray(value);
546 if (isArrayMultiLine) {
547 writeWithIndent("[");
549 bool hasChildValue = !childValues_.empty();
552 const Value& childValue = value[index];
553 writeCommentBeforeValue(childValue);
555 writeWithIndent(childValues_[index]);
557 if (!indented_) writeIndent();
559 writeValue(childValue);
562 if (++index == size) {
563 writeCommentAfterValueOnSameLine(childValue);
567 writeCommentAfterValueOnSameLine(childValue);
570 writeWithIndent("]");
571 } else // output on a single line
573 assert(childValues_.size() == size);
575 for (unsigned index = 0; index < size; ++index) {
578 *document_ << childValues_[index];
585 bool StyledStreamWriter::isMultineArray(const Value& value) {
586 int size = value.size();
587 bool isMultiLine = size * 3 >= rightMargin_;
588 childValues_.clear();
589 for (int index = 0; index < size && !isMultiLine; ++index) {
590 const Value& childValue = value[index];
592 isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
593 childValue.size() > 0);
595 if (!isMultiLine) // check if line length > max line length
597 childValues_.reserve(size);
598 addChildValues_ = true;
599 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
600 for (int index = 0; index < size; ++index) {
601 if (hasCommentForValue(value[index])) {
604 writeValue(value[index]);
605 lineLength += int(childValues_[index].length());
607 addChildValues_ = false;
608 isMultiLine = isMultiLine || lineLength >= rightMargin_;
613 void StyledStreamWriter::pushValue(const std::string& value) {
615 childValues_.push_back(value);
620 void StyledStreamWriter::writeIndent() {
621 // blep intended this to look at the so-far-written string
622 // to determine whether we are already indented, but
623 // with a stream we cannot do that. So we rely on some saved state.
624 // The caller checks indented_.
625 *document_ << '\n' << indentString_;
628 void StyledStreamWriter::writeWithIndent(const std::string& value) {
629 if (!indented_) writeIndent();
634 void StyledStreamWriter::indent() { indentString_ += indentation_; }
636 void StyledStreamWriter::unindent() {
637 assert(indentString_.size() >= indentation_.size());
638 indentString_.resize(indentString_.size() - indentation_.size());
641 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
642 if (!root.hasComment(commentBefore))
645 if (!indented_) writeIndent();
646 const std::string& comment = root.getComment(commentBefore);
647 std::string::const_iterator iter = comment.begin();
648 while (iter != comment.end()) {
651 (iter != comment.end() && *(iter + 1) == '/'))
652 // writeIndent(); // would include newline
653 *document_ << indentString_;
659 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
660 if (root.hasComment(commentAfterOnSameLine))
661 *document_ << ' ' << root.getComment(commentAfterOnSameLine);
663 if (root.hasComment(commentAfter)) {
665 *document_ << root.getComment(commentAfter);
670 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
671 return value.hasComment(commentBefore) ||
672 value.hasComment(commentAfterOnSameLine) ||
673 value.hasComment(commentAfter);
676 //////////////////////////
677 // BuiltStyledStreamWriter
679 /// Scoped enums are not available until C++11.
680 struct CommentStyle {
681 /// Decide whether to write comments.
683 None, ///< Drop all comments.
684 Most, ///< Recover odd behavior of previous versions (not implemented yet).
685 All ///< Keep all comments.
689 struct BuiltStyledStreamWriter : public StreamWriter
691 BuiltStyledStreamWriter(
692 std::string const& indentation,
693 CommentStyle::Enum cs,
694 std::string const& colonSymbol,
695 std::string const& nullSymbol,
696 std::string const& endingLineFeedSymbol);
697 virtual int write(Value const& root, std::ostream* sout);
699 void writeValue(Value const& value);
700 void writeArrayValue(Value const& value);
701 bool isMultineArray(Value const& value);
702 void pushValue(std::string const& value);
704 void writeWithIndent(std::string const& value);
707 void writeCommentBeforeValue(Value const& root);
708 void writeCommentAfterValueOnSameLine(Value const& root);
709 static bool hasCommentForValue(const Value& value);
711 typedef std::vector<std::string> ChildValues;
713 ChildValues childValues_;
714 std::string indentString_;
716 std::string indentation_;
717 CommentStyle::Enum cs_;
718 std::string colonSymbol_;
719 std::string nullSymbol_;
720 std::string endingLineFeedSymbol_;
721 bool addChildValues_ : 1;
724 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
725 std::string const& indentation,
726 CommentStyle::Enum cs,
727 std::string const& colonSymbol,
728 std::string const& nullSymbol,
729 std::string const& endingLineFeedSymbol)
731 , indentation_(indentation)
733 , colonSymbol_(colonSymbol)
734 , nullSymbol_(nullSymbol)
735 , endingLineFeedSymbol_(endingLineFeedSymbol)
736 , addChildValues_(false)
740 int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
743 addChildValues_ = false;
746 writeCommentBeforeValue(root);
747 if (!indented_) writeIndent();
750 writeCommentAfterValueOnSameLine(root);
751 *sout_ << endingLineFeedSymbol_;
755 void BuiltStyledStreamWriter::writeValue(Value const& value) {
756 switch (value.type()) {
758 pushValue(nullSymbol_);
761 pushValue(valueToString(value.asLargestInt()));
764 pushValue(valueToString(value.asLargestUInt()));
767 pushValue(valueToString(value.asDouble()));
770 pushValue(valueToQuotedString(value.asCString()));
773 pushValue(valueToString(value.asBool()));
776 writeArrayValue(value);
779 Value::Members members(value.getMemberNames());
783 writeWithIndent("{");
785 Value::Members::iterator it = members.begin();
787 std::string const& name = *it;
788 Value const& childValue = value[name];
789 writeCommentBeforeValue(childValue);
790 writeWithIndent(valueToQuotedString(name.c_str()));
791 *sout_ << colonSymbol_;
792 writeValue(childValue);
793 if (++it == members.end()) {
794 writeCommentAfterValueOnSameLine(childValue);
798 writeCommentAfterValueOnSameLine(childValue);
801 writeWithIndent("}");
807 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
808 unsigned size = value.size();
812 bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
814 writeWithIndent("[");
816 bool hasChildValue = !childValues_.empty();
819 Value const& childValue = value[index];
820 writeCommentBeforeValue(childValue);
822 writeWithIndent(childValues_[index]);
824 if (!indented_) writeIndent();
826 writeValue(childValue);
829 if (++index == size) {
830 writeCommentAfterValueOnSameLine(childValue);
834 writeCommentAfterValueOnSameLine(childValue);
837 writeWithIndent("]");
838 } else // output on a single line
840 assert(childValues_.size() == size);
842 if (!indentation_.empty()) *sout_ << " ";
843 for (unsigned index = 0; index < size; ++index) {
846 *sout_ << childValues_[index];
848 if (!indentation_.empty()) *sout_ << " ";
854 bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
855 int size = value.size();
856 bool isMultiLine = size * 3 >= rightMargin_;
857 childValues_.clear();
858 for (int index = 0; index < size && !isMultiLine; ++index) {
859 Value const& childValue = value[index];
861 isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
862 childValue.size() > 0);
864 if (!isMultiLine) // check if line length > max line length
866 childValues_.reserve(size);
867 addChildValues_ = true;
868 int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
869 for (int index = 0; index < size; ++index) {
870 if (hasCommentForValue(value[index])) {
873 writeValue(value[index]);
874 lineLength += int(childValues_[index].length());
876 addChildValues_ = false;
877 isMultiLine = isMultiLine || lineLength >= rightMargin_;
882 void BuiltStyledStreamWriter::pushValue(std::string const& value) {
884 childValues_.push_back(value);
889 void BuiltStyledStreamWriter::writeIndent() {
890 // blep intended this to look at the so-far-written string
891 // to determine whether we are already indented, but
892 // with a stream we cannot do that. So we rely on some saved state.
893 // The caller checks indented_.
895 if (!indentation_.empty()) {
896 // In this case, drop newlines too.
897 *sout_ << '\n' << indentString_;
901 void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
902 if (!indented_) writeIndent();
907 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
909 void BuiltStyledStreamWriter::unindent() {
910 assert(indentString_.size() >= indentation_.size());
911 indentString_.resize(indentString_.size() - indentation_.size());
914 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
915 if (cs_ == CommentStyle::None) return;
916 if (!root.hasComment(commentBefore))
919 if (!indented_) writeIndent();
920 const std::string& comment = root.getComment(commentBefore);
921 std::string::const_iterator iter = comment.begin();
922 while (iter != comment.end()) {
925 (iter != comment.end() && *(iter + 1) == '/'))
926 // writeIndent(); // would write extra newline
927 *sout_ << indentString_;
933 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
934 if (cs_ == CommentStyle::None) return;
935 if (root.hasComment(commentAfterOnSameLine))
936 *sout_ << " " + root.getComment(commentAfterOnSameLine);
938 if (root.hasComment(commentAfter)) {
940 *sout_ << root.getComment(commentAfter);
945 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
946 return value.hasComment(commentBefore) ||
947 value.hasComment(commentAfterOnSameLine) ||
948 value.hasComment(commentAfter);
954 StreamWriter::StreamWriter()
958 StreamWriter::~StreamWriter()
961 StreamWriter::Factory::~Factory()
963 StreamWriterBuilder::StreamWriterBuilder()
965 setDefaults(&settings_);
967 StreamWriterBuilder::~StreamWriterBuilder()
969 StreamWriter* StreamWriterBuilder::newStreamWriter() const
971 std::string indentation = settings_["indentation"].asString();
972 std::string cs_str = settings_["commentStyle"].asString();
973 bool eyc = settings_["enableYAMLCompatibility"].asBool();
974 bool dnp = settings_["dropNullPlaceholders"].asBool();
975 CommentStyle::Enum cs = CommentStyle::All;
976 if (cs_str == "All") {
977 cs = CommentStyle::All;
978 } else if (cs_str == "None") {
979 cs = CommentStyle::None;
981 throw std::runtime_error("commentStyle must be 'All' or 'None'");
983 std::string colonSymbol = " : ";
986 } else if (indentation.empty()) {
989 std::string nullSymbol = "null";
993 std::string endingLineFeedSymbol = "";
994 return new BuiltStyledStreamWriter(
996 colonSymbol, nullSymbol, endingLineFeedSymbol);
998 static void getValidWriterKeys(std::set<std::string>* valid_keys)
1000 valid_keys->clear();
1001 valid_keys->insert("indentation");
1002 valid_keys->insert("commentStyle");
1003 valid_keys->insert("enableYAMLCompatibility");
1004 valid_keys->insert("dropNullPlaceholders");
1006 bool StreamWriterBuilder::validate(Json::Value* invalid) const
1008 Json::Value my_invalid;
1009 if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
1010 Json::Value& inv = *invalid;
1012 std::set<std::string> valid_keys;
1013 getValidWriterKeys(&valid_keys);
1014 Value::Members keys = settings_.getMemberNames();
1015 size_t n = keys.size();
1016 for (size_t i = 0; i < n; ++i) {
1017 std::string const& key = keys[i];
1018 if (valid_keys.find(key) == valid_keys.end()) {
1019 inv[key] = settings_[key];
1025 void StreamWriterBuilder::setDefaults(Json::Value* settings)
1027 //! [StreamWriterBuilderDefaults]
1028 (*settings)["commentStyle"] = "All";
1029 (*settings)["indentation"] = "\t";
1030 (*settings)["enableYAMLCompatibility"] = false;
1031 (*settings)["dropNullPlaceholders"] = false;
1032 //! [StreamWriterBuilderDefaults]
1035 std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
1036 std::ostringstream sout;
1037 StreamWriterPtr const writer(builder.newStreamWriter());
1038 writer->write(root, &sout);
1042 std::ostream& operator<<(std::ostream& sout, Value const& root) {
1043 StreamWriterBuilder builder;
1044 StreamWriterPtr const writer(builder.newStreamWriter());
1045 writer->write(root, &sout);