1 #ifndef JSON_SPIRIT_WRITER_TEMPLATE
\r
2 #define JSON_SPIRIT_WRITER_TEMPLATE
\r
4 // Copyright John W. Wilkinson 2007 - 2011
\r
5 // Distributed under the MIT License, see accompanying file LICENSE.txt
\r
7 // json spirit version 4.05
\r
9 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
\r
13 #include "json_spirit_value.h"
\r
14 #include "json_spirit_writer_options.h"
\r
19 #include <boost/io/ios_state.hpp>
\r
21 namespace json_spirit
\r
23 inline char to_hex_char( unsigned int c )
\r
27 const char ch = static_cast< char >( c );
\r
29 if( ch < 10 ) return '0' + ch;
\r
31 return 'A' - 10 + ch;
\r
34 template< class String_type >
\r
35 String_type non_printable_to_string( unsigned int c )
\r
37 String_type result( 6, '\\' );
\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
49 template< typename Char_type, class String_type >
\r
50 bool add_esc_char( Char_type c, String_type& s )
\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
66 template< class String_type >
\r
67 String_type add_esc_chars( const String_type& s, bool raw_utf8 )
\r
69 typedef typename String_type::const_iterator Iter_type;
\r
70 typedef typename String_type::value_type Char_type;
\r
74 const Iter_type end( s.end() );
\r
76 for( Iter_type i = s.begin(); i != end; ++i )
\r
78 const Char_type c( *i );
\r
80 if( add_esc_char( c, result ) ) continue;
\r
88 const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
\r
90 if( iswprint( unsigned_c ) )
\r
96 result += non_printable_to_string< String_type >( unsigned_c );
\r
104 template< class Ostream >
\r
105 void append_double( Ostream& os, const double d, const int precision )
\r
107 os << std::showpoint << std::setprecision( precision ) << d;
\r
110 template< class String_type >
\r
111 void erase_and_extract_exponent( String_type& str, String_type& exp )
\r
113 const typename String_type::size_type exp_start= str.find( 'e' );
\r
115 if( exp_start != String_type::npos )
\r
117 exp = str.substr( exp_start );
\r
118 str.erase( exp_start );
\r
122 template< class String_type >
\r
123 typename String_type::size_type find_first_non_zero( const String_type& str )
\r
125 typename String_type::size_type result = str.size() - 1;
\r
127 for( ; result != 0; --result )
\r
129 if( str[ result ] != '0' )
\r
138 template< class String_type >
\r
139 void remove_trailing( String_type& str )
\r
143 erase_and_extract_exponent( str, exp );
\r
145 const typename String_type::size_type first_non_zero = find_first_non_zero( str );
\r
147 if( first_non_zero != 0 )
\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
156 // this class generates the JSON text,
\r
157 // it keeps track of the indentation level etc.
\r
159 template< class Value_type, class Ostream_type >
\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
171 Generator( const Value_type& value, Ostream_type& os, unsigned int options )
\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
185 void output( const Value_type& value )
\r
187 switch( value.type() )
\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
200 void output( const Object_type& obj )
\r
202 output_array_or_obj( obj, '{', '}' );
\r
205 void output( const Obj_member_type& member )
\r
207 output( Config_type::get_name( member ) ); space();
\r
208 os_ << ':'; space();
\r
209 output( Config_type::get_value( member ) );
\r
212 void output_int( const Value_type& value )
\r
214 if( value.is_uint64() )
\r
216 os_ << value.get_uint64();
\r
220 os_ << value.get_int64();
\r
224 void output( const String_type& s )
\r
226 os_ << '"' << add_esc_chars( s, raw_utf8_ ) << '"';
\r
229 void output( bool b )
\r
231 os_ << to_str< String_type >( b ? "true" : "false" );
\r
234 void output( double d )
\r
236 if( remove_trailing_zeros_ )
\r
238 std::basic_ostringstream< Char_type > os;
\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
243 String_type str = os.str();
\r
245 remove_trailing( str );
\r
251 append_double( os_, d, 17 );
\r
255 static bool contains_composite_elements( const Array_type& arr )
\r
257 for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
\r
259 const Value_type& val = *i;
\r
261 if( val.type() == obj_type ||
\r
262 val.type() == array_type )
\r
271 template< class Iter >
\r
272 void output_composite_item( Iter i, Iter last )
\r
282 void output( const Array_type& arr )
\r
284 if( single_line_arrays_ && !contains_composite_elements( arr ) )
\r
286 os_ << '['; space();
\r
288 for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
\r
290 output_composite_item( i, arr.end() );
\r
299 output_array_or_obj( arr, '[', ']' );
\r
303 template< class T >
\r
304 void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
\r
306 os_ << start_char; new_line();
\r
308 ++indentation_level_;
\r
310 for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
\r
314 output_composite_item( i, t.end() );
\r
319 --indentation_level_;
\r
321 indent(); os_ << end_char;
\r
326 if( !pretty_ ) return;
\r
328 for( int i = 0; i < indentation_level_; ++i )
\r
336 if( pretty_ ) os_ << ' ';
\r
341 if( pretty_ ) os_ << '\n';
\r
344 Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
\r
347 int indentation_level_;
\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
355 // writes JSON Value to a stream, e.g.
\r
357 // write_stream( value, os, pretty_print );
\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
363 Generator< Value_type, Ostream_type >( value, os, options );
\r
366 // writes JSON Value to a stream, e.g.
\r
368 // const string json_str = write( value, pretty_print );
\r
370 template< class Value_type >
\r
371 typename Value_type::String_type write_string( const Value_type& value, unsigned int options = 0 )
\r
373 typedef typename Value_type::String_type::value_type Char_type;
\r
375 std::basic_ostringstream< Char_type > os;
\r
377 write_stream( value, os, options );
\r