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