]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #ifndef JSON_SPIRIT_WRITER_TEMPLATE\r |
2 | #define JSON_SPIRIT_WRITER_TEMPLATE\r | |
3 | \r | |
4 | // Copyright John W. Wilkinson 2007 - 2011\r | |
5 | // Distributed under the MIT License, see accompanying file LICENSE.txt\r | |
6 | \r | |
7 | // json spirit version 4.05\r | |
8 | \r | |
9 | #if defined(_MSC_VER) && (_MSC_VER >= 1020)\r | |
10 | # pragma once\r | |
11 | #endif\r | |
12 | \r | |
13 | #include "json_spirit_value.h"\r | |
14 | #include "json_spirit_writer_options.h"\r | |
15 | \r | |
16 | #include <cassert>\r | |
17 | #include <sstream>\r | |
18 | #include <iomanip>\r | |
19 | #include <boost/io/ios_state.hpp>\r | |
20 | \r | |
21 | namespace json_spirit\r | |
22 | {\r | |
23 | inline char to_hex_char( unsigned int c )\r | |
24 | {\r | |
25 | assert( c <= 0xF );\r | |
26 | \r | |
27 | const char ch = static_cast< char >( c );\r | |
28 | \r | |
29 | if( ch < 10 ) return '0' + ch;\r | |
30 | \r | |
31 | return 'A' - 10 + ch;\r | |
32 | }\r | |
33 | \r | |
34 | template< class String_type >\r | |
35 | String_type non_printable_to_string( unsigned int c )\r | |
36 | {\r | |
37 | String_type result( 6, '\\' );\r | |
38 | \r | |
39 | result[1] = 'u';\r | |
40 | \r | |
41 | result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;\r | |
42 | result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;\r | |
43 | result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;\r | |
44 | result[ 2 ] = to_hex_char( c & 0x000F );\r | |
45 | \r | |
46 | return result;\r | |
47 | }\r | |
48 | \r | |
49 | template< typename Char_type, class String_type >\r | |
50 | bool add_esc_char( Char_type c, String_type& s )\r | |
51 | {\r | |
52 | switch( c )\r | |
53 | {\r | |
54 | case '"': s += to_str< String_type >( "\\\"" ); return true;\r | |
55 | case '\\': s += to_str< String_type >( "\\\\" ); return true;\r | |
56 | case '\b': s += to_str< String_type >( "\\b" ); return true;\r | |
57 | case '\f': s += to_str< String_type >( "\\f" ); return true;\r | |
58 | case '\n': s += to_str< String_type >( "\\n" ); return true;\r | |
59 | case '\r': s += to_str< String_type >( "\\r" ); return true;\r | |
60 | case '\t': s += to_str< String_type >( "\\t" ); return true;\r | |
61 | }\r | |
62 | \r | |
63 | return false;\r | |
64 | }\r | |
65 | \r | |
66 | template< class String_type >\r | |
67 | String_type add_esc_chars( const String_type& s, bool raw_utf8 )\r | |
68 | {\r | |
69 | typedef typename String_type::const_iterator Iter_type;\r | |
70 | typedef typename String_type::value_type Char_type;\r | |
71 | \r | |
72 | String_type result;\r | |
73 | \r | |
74 | const Iter_type end( s.end() );\r | |
75 | \r | |
76 | for( Iter_type i = s.begin(); i != end; ++i )\r | |
77 | {\r | |
78 | const Char_type c( *i );\r | |
79 | \r | |
80 | if( add_esc_char( c, result ) ) continue;\r | |
81 | \r | |
82 | if( raw_utf8 )\r | |
83 | {\r | |
84 | result += c;\r | |
85 | }\r | |
86 | else\r | |
87 | {\r | |
88 | const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );\r | |
89 | \r | |
90 | if( iswprint( unsigned_c ) )\r | |
91 | {\r | |
92 | result += c;\r | |
93 | }\r | |
94 | else\r | |
95 | {\r | |
96 | result += non_printable_to_string< String_type >( unsigned_c );\r | |
97 | }\r | |
98 | }\r | |
99 | }\r | |
100 | \r | |
101 | return result;\r | |
102 | }\r | |
103 | \r | |
104 | template< class Ostream >\r | |
105 | void append_double( Ostream& os, const double d, const int precision )\r | |
106 | {\r | |
107 | os << std::showpoint << std::setprecision( precision ) << d;\r | |
108 | }\r | |
109 | \r | |
110 | template< class String_type >\r | |
111 | void erase_and_extract_exponent( String_type& str, String_type& exp )\r | |
112 | {\r | |
113 | const typename String_type::size_type exp_start= str.find( 'e' );\r | |
114 | \r | |
115 | if( exp_start != String_type::npos )\r | |
116 | {\r | |
117 | exp = str.substr( exp_start );\r | |
118 | str.erase( exp_start );\r | |
119 | }\r | |
120 | }\r | |
121 | \r | |
122 | template< class String_type >\r | |
123 | typename String_type::size_type find_first_non_zero( const String_type& str )\r | |
124 | {\r | |
125 | typename String_type::size_type result = str.size() - 1;\r | |
126 | \r | |
127 | for( ; result != 0; --result )\r | |
128 | {\r | |
129 | if( str[ result ] != '0' )\r | |
130 | {\r | |
131 | break;\r | |
132 | }\r | |
133 | }\r | |
134 | \r | |
135 | return result;\r | |
136 | }\r | |
137 | \r | |
138 | template< class String_type >\r | |
139 | void remove_trailing( String_type& str )\r | |
140 | {\r | |
141 | String_type exp;\r | |
142 | \r | |
143 | erase_and_extract_exponent( str, exp );\r | |
144 | \r | |
145 | const typename String_type::size_type first_non_zero = find_first_non_zero( str );\r | |
146 | \r | |
147 | if( first_non_zero != 0 )\r | |
148 | {\r | |
149 | const int offset = str[first_non_zero] == '.' ? 2 : 1; // note zero digits following a decimal point is non standard\r | |
150 | str.erase( first_non_zero + offset );\r | |
151 | }\r | |
152 | \r | |
153 | str += exp;\r | |
154 | }\r | |
155 | \r | |
156 | // this class generates the JSON text,\r | |
157 | // it keeps track of the indentation level etc.\r | |
158 | //\r | |
159 | template< class Value_type, class Ostream_type >\r | |
160 | class Generator\r | |
161 | {\r | |
162 | typedef typename Value_type::Config_type Config_type;\r | |
163 | typedef typename Config_type::String_type String_type;\r | |
164 | typedef typename Config_type::Object_type Object_type;\r | |
165 | typedef typename Config_type::Array_type Array_type;\r | |
166 | typedef typename String_type::value_type Char_type;\r | |
167 | typedef typename Object_type::value_type Obj_member_type;\r | |
168 | \r | |
169 | public:\r | |
170 | \r | |
171 | Generator( const Value_type& value, Ostream_type& os, unsigned int options )\r | |
172 | : os_( os )\r | |
173 | , indentation_level_( 0 )\r | |
174 | , pretty_( ( options & pretty_print ) != 0 || ( options & single_line_arrays ) != 0 )\r | |
175 | , raw_utf8_( ( options & raw_utf8 ) != 0 )\r | |
176 | , remove_trailing_zeros_( ( options & remove_trailing_zeros ) != 0 )\r | |
177 | , single_line_arrays_( ( options & single_line_arrays ) != 0 )\r | |
178 | , ios_saver_( os )\r | |
179 | {\r | |
180 | output( value );\r | |
181 | }\r | |
182 | \r | |
183 | private:\r | |
184 | \r | |
185 | void output( const Value_type& value )\r | |
186 | {\r | |
187 | switch( value.type() )\r | |
188 | {\r | |
189 | case obj_type: output( value.get_obj() ); break;\r | |
190 | case array_type: output( value.get_array() ); break;\r | |
191 | case str_type: output( value.get_str() ); break;\r | |
192 | case bool_type: output( value.get_bool() ); break;\r | |
193 | case real_type: output( value.get_real() ); break;\r | |
194 | case int_type: output_int( value ); break;\r | |
195 | case null_type: os_ << "null"; break;\r | |
196 | default: assert( false );\r | |
197 | }\r | |
198 | }\r | |
199 | \r | |
200 | void output( const Object_type& obj )\r | |
201 | {\r | |
202 | output_array_or_obj( obj, '{', '}' );\r | |
203 | }\r | |
204 | \r | |
205 | void output( const Obj_member_type& member )\r | |
206 | {\r | |
207 | output( Config_type::get_name( member ) ); space(); \r | |
208 | os_ << ':'; space(); \r | |
209 | output( Config_type::get_value( member ) );\r | |
210 | }\r | |
211 | \r | |
212 | void output_int( const Value_type& value )\r | |
213 | {\r | |
214 | if( value.is_uint64() )\r | |
215 | {\r | |
216 | os_ << value.get_uint64();\r | |
217 | }\r | |
218 | else\r | |
219 | {\r | |
220 | os_ << value.get_int64();\r | |
221 | }\r | |
222 | }\r | |
223 | \r | |
224 | void output( const String_type& s )\r | |
225 | {\r | |
226 | os_ << '"' << add_esc_chars( s, raw_utf8_ ) << '"';\r | |
227 | }\r | |
228 | \r | |
229 | void output( bool b )\r | |
230 | {\r | |
231 | os_ << to_str< String_type >( b ? "true" : "false" );\r | |
232 | }\r | |
233 | \r | |
234 | void output( double d )\r | |
235 | {\r | |
236 | if( remove_trailing_zeros_ )\r | |
237 | {\r | |
238 | std::basic_ostringstream< Char_type > os;\r | |
239 | \r | |
240 | append_double( os, d, 16 ); // note precision is 16 so that we get some trailing space that we can remove,\r | |
241 | // otherwise, 0.1234 gets converted to "0.12399999..."\r | |
242 | \r | |
243 | String_type str = os.str();\r | |
244 | \r | |
245 | remove_trailing( str );\r | |
246 | \r | |
247 | os_ << str;\r | |
248 | }\r | |
249 | else\r | |
250 | {\r | |
251 | append_double( os_, d, 17 );\r | |
252 | }\r | |
253 | }\r | |
254 | \r | |
255 | static bool contains_composite_elements( const Array_type& arr )\r | |
256 | {\r | |
257 | for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )\r | |
258 | {\r | |
259 | const Value_type& val = *i;\r | |
260 | \r | |
261 | if( val.type() == obj_type ||\r | |
262 | val.type() == array_type )\r | |
263 | {\r | |
264 | return true;\r | |
265 | }\r | |
266 | }\r | |
267 | \r | |
268 | return false;\r | |
269 | }\r | |
270 | \r | |
271 | template< class Iter >\r | |
272 | void output_composite_item( Iter i, Iter last )\r | |
273 | {\r | |
274 | output( *i );\r | |
275 | \r | |
276 | if( ++i != last )\r | |
277 | {\r | |
278 | os_ << ',';\r | |
279 | }\r | |
280 | }\r | |
281 | \r | |
282 | void output( const Array_type& arr )\r | |
283 | {\r | |
284 | if( single_line_arrays_ && !contains_composite_elements( arr ) )\r | |
285 | {\r | |
286 | os_ << '['; space();\r | |
287 | \r | |
288 | for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )\r | |
289 | {\r | |
290 | output_composite_item( i, arr.end() );\r | |
291 | \r | |
292 | space();\r | |
293 | }\r | |
294 | \r | |
295 | os_ << ']';\r | |
296 | }\r | |
297 | else\r | |
298 | {\r | |
299 | output_array_or_obj( arr, '[', ']' );\r | |
300 | }\r | |
301 | }\r | |
302 | \r | |
303 | template< class T >\r | |
304 | void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )\r | |
305 | {\r | |
306 | os_ << start_char; new_line();\r | |
307 | \r | |
308 | ++indentation_level_;\r | |
309 | \r | |
310 | for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )\r | |
311 | {\r | |
312 | indent();\r | |
313 | \r | |
314 | output_composite_item( i, t.end() );\r | |
315 | \r | |
316 | new_line();\r | |
317 | }\r | |
318 | \r | |
319 | --indentation_level_;\r | |
320 | \r | |
321 | indent(); os_ << end_char;\r | |
322 | }\r | |
323 | \r | |
324 | void indent()\r | |
325 | {\r | |
326 | if( !pretty_ ) return;\r | |
327 | \r | |
328 | for( int i = 0; i < indentation_level_; ++i )\r | |
329 | { \r | |
330 | os_ << " ";\r | |
331 | }\r | |
332 | }\r | |
333 | \r | |
334 | void space()\r | |
335 | {\r | |
336 | if( pretty_ ) os_ << ' ';\r | |
337 | }\r | |
338 | \r | |
339 | void new_line()\r | |
340 | {\r | |
341 | if( pretty_ ) os_ << '\n';\r | |
342 | }\r | |
343 | \r | |
344 | Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning\r | |
345 | \r | |
346 | Ostream_type& os_;\r | |
347 | int indentation_level_;\r | |
348 | bool pretty_;\r | |
349 | bool raw_utf8_;\r | |
350 | bool remove_trailing_zeros_;\r | |
351 | bool single_line_arrays_;\r | |
352 | boost::io::basic_ios_all_saver< Char_type > ios_saver_; // so that ostream state is reset after control is returned to the caller\r | |
353 | };\r | |
354 | \r | |
355 | // writes JSON Value to a stream, e.g.\r | |
356 | //\r | |
357 | // write_stream( value, os, pretty_print );\r | |
358 | //\r | |
359 | template< class Value_type, class Ostream_type >\r | |
360 | void write_stream( const Value_type& value, Ostream_type& os, unsigned int options = 0 )\r | |
361 | {\r | |
362 | os << std::dec;\r | |
363 | Generator< Value_type, Ostream_type >( value, os, options );\r | |
364 | }\r | |
365 | \r | |
366 | // writes JSON Value to a stream, e.g.\r | |
367 | //\r | |
368 | // const string json_str = write( value, pretty_print );\r | |
369 | //\r | |
370 | template< class Value_type >\r | |
371 | typename Value_type::String_type write_string( const Value_type& value, unsigned int options = 0 )\r | |
372 | {\r | |
373 | typedef typename Value_type::String_type::value_type Char_type;\r | |
374 | \r | |
375 | std::basic_ostringstream< Char_type > os;\r | |
376 | \r | |
377 | write_stream( value, os, options );\r | |
378 | \r | |
379 | return os.str();\r | |
380 | }\r | |
381 | }\r | |
382 | \r | |
383 | #endif\r |