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