write.hpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // ----------------------------------------------------------------------------
  2. // Copyright (C) 2002-2006 Marcin Kalicinski
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. // For more information, see www.boost.org
  9. // ----------------------------------------------------------------------------
  10. #ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_WRITE_HPP_INCLUDED
  11. #define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_WRITE_HPP_INCLUDED
  12. #include <boost/property_tree/json_parser/error.hpp>
  13. #include <boost/property_tree/ptree.hpp>
  14. #include <boost/next_prior.hpp>
  15. #include <boost/type_traits/make_unsigned.hpp>
  16. #include <string>
  17. #include <ostream>
  18. #include <iomanip>
  19. namespace boost { namespace property_tree { namespace json_parser
  20. {
  21. // Create necessary escape sequences from illegal characters
  22. template<class Ch>
  23. std::basic_string<Ch> create_escapes(const std::basic_string<Ch> &s)
  24. {
  25. std::basic_string<Ch> result;
  26. typename std::basic_string<Ch>::const_iterator b = s.begin();
  27. typename std::basic_string<Ch>::const_iterator e = s.end();
  28. while (b != e)
  29. {
  30. typedef typename make_unsigned<Ch>::type UCh;
  31. UCh c(*b);
  32. // This assumes an ASCII superset. But so does everything in PTree.
  33. // We escape everything outside ASCII, because this code can't
  34. // handle high unicode characters.
  35. if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) ||
  36. (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0xFF))
  37. result += *b;
  38. else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b');
  39. else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f');
  40. else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n');
  41. else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r');
  42. else if (*b == Ch('\t')) result += Ch('\\'), result += Ch('t');
  43. else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/');
  44. else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"');
  45. else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\');
  46. else
  47. {
  48. const char *hexdigits = "0123456789ABCDEF";
  49. unsigned long u = (std::min)(static_cast<unsigned long>(
  50. static_cast<UCh>(*b)),
  51. 0xFFFFul);
  52. unsigned long d1 = u / 4096; u -= d1 * 4096;
  53. unsigned long d2 = u / 256; u -= d2 * 256;
  54. unsigned long d3 = u / 16; u -= d3 * 16;
  55. unsigned long d4 = u;
  56. result += Ch('\\'); result += Ch('u');
  57. result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]);
  58. result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]);
  59. }
  60. ++b;
  61. }
  62. return result;
  63. }
  64. template<class Ptree>
  65. void write_json_helper(std::basic_ostream<typename Ptree::key_type::value_type> &stream,
  66. const Ptree &pt,
  67. int indent, bool pretty)
  68. {
  69. typedef typename Ptree::key_type::value_type Ch;
  70. typedef typename std::basic_string<Ch> Str;
  71. // Value or object or array
  72. if (indent > 0 && pt.empty())
  73. {
  74. // Write value
  75. Str data = create_escapes(pt.template get_value<Str>());
  76. stream << Ch('"') << data << Ch('"');
  77. }
  78. else if (indent > 0 && pt.count(Str()) == pt.size())
  79. {
  80. // Write array
  81. stream << Ch('[');
  82. if (pretty) stream << Ch('\n');
  83. typename Ptree::const_iterator it = pt.begin();
  84. for (; it != pt.end(); ++it)
  85. {
  86. if (pretty) stream << Str(4 * (indent + 1), Ch(' '));
  87. write_json_helper(stream, it->second, indent + 1, pretty);
  88. if (boost::next(it) != pt.end())
  89. stream << Ch(',');
  90. if (pretty) stream << Ch('\n');
  91. }
  92. if (pretty) stream << Str(4 * indent, Ch(' '));
  93. stream << Ch(']');
  94. }
  95. else
  96. {
  97. // Write object
  98. stream << Ch('{');
  99. if (pretty) stream << Ch('\n');
  100. typename Ptree::const_iterator it = pt.begin();
  101. for (; it != pt.end(); ++it)
  102. {
  103. if (pretty) stream << Str(4 * (indent + 1), Ch(' '));
  104. stream << Ch('"') << create_escapes(it->first) << Ch('"') << Ch(':');
  105. if (pretty) stream << Ch(' ');
  106. write_json_helper(stream, it->second, indent + 1, pretty);
  107. if (boost::next(it) != pt.end())
  108. stream << Ch(',');
  109. if (pretty) stream << Ch('\n');
  110. }
  111. if (pretty) stream << Str(4 * indent, Ch(' '));
  112. stream << Ch('}');
  113. }
  114. }
  115. // Verify if ptree does not contain information that cannot be written to json
  116. template<class Ptree>
  117. bool verify_json(const Ptree &pt, int depth)
  118. {
  119. typedef typename Ptree::key_type::value_type Ch;
  120. typedef typename std::basic_string<Ch> Str;
  121. // Root ptree cannot have data
  122. if (depth == 0 && !pt.template get_value<Str>().empty())
  123. return false;
  124. // Ptree cannot have both children and data
  125. if (!pt.template get_value<Str>().empty() && !pt.empty())
  126. return false;
  127. // Check children
  128. typename Ptree::const_iterator it = pt.begin();
  129. for (; it != pt.end(); ++it)
  130. if (!verify_json(it->second, depth + 1))
  131. return false;
  132. // Success
  133. return true;
  134. }
  135. // Write ptree to json stream
  136. template<class Ptree>
  137. void write_json_internal(std::basic_ostream<typename Ptree::key_type::value_type> &stream,
  138. const Ptree &pt,
  139. const std::string &filename,
  140. bool pretty)
  141. {
  142. if (!verify_json(pt, 0))
  143. BOOST_PROPERTY_TREE_THROW(json_parser_error("ptree contains data that cannot be represented in JSON format", filename, 0));
  144. write_json_helper(stream, pt, 0, pretty);
  145. if (pretty) stream << std::endl;
  146. else stream << std::flush;
  147. if (!stream.good())
  148. BOOST_PROPERTY_TREE_THROW(json_parser_error("write error", filename, 0));
  149. }
  150. } } }
  151. #endif