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