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