diff --git a/include/fc/io/json.hpp b/include/fc/io/json.hpp index f423803..8a46d13 100644 --- a/include/fc/io/json.hpp +++ b/include/fc/io/json.hpp @@ -19,30 +19,36 @@ namespace fc { legacy_parser = 0, strict_parser = 1, - relaxed_parser = 2 + relaxed_parser = 2, + legacy_parser_with_string_doubles = 3 + }; + enum output_formatting + { + stringify_large_ints_and_doubles = 0, + legacy_generator = 1 }; - static ostream& to_stream( ostream& out, const fc::string& ); - static ostream& to_stream( ostream& out, const variant& v ); - static ostream& to_stream( ostream& out, const variants& v ); - static ostream& to_stream( ostream& out, const variant_object& v ); + static ostream& to_stream( ostream& out, const fc::string&); + static ostream& to_stream( ostream& out, const variant& v, output_formatting format = stringify_large_ints_and_doubles ); + static ostream& to_stream( ostream& out, const variants& v, output_formatting format = stringify_large_ints_and_doubles ); + static ostream& to_stream( ostream& out, const variant_object& v, output_formatting format = stringify_large_ints_and_doubles ); static variant from_stream( buffered_istream& in, parse_type ptype = legacy_parser ); static variant from_string( const string& utf8_str, parse_type ptype = legacy_parser ); static variants variants_from_string( const string& utf8_str, parse_type ptype = legacy_parser ); - static string to_string( const variant& v ); - static string to_pretty_string( const variant& v ); + static string to_string( const variant& v, output_formatting format = stringify_large_ints_and_doubles ); + static string to_pretty_string( const variant& v, output_formatting format = stringify_large_ints_and_doubles ); static bool is_valid( const std::string& json_str, parse_type ptype = legacy_parser ); template - static void save_to_file( const T& v, const fc::path& fi, bool pretty = true ) + static void save_to_file( const T& v, const fc::path& fi, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ) { - save_to_file( variant(v), fi, pretty ); + save_to_file( variant(v), fi, pretty, format ); } - static void save_to_file( const variant& v, const fc::path& fi, bool pretty = true ); + static void save_to_file( const variant& v, const fc::path& fi, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ); static variant from_file( const fc::path& p, parse_type ptype = legacy_parser ); template @@ -52,19 +58,19 @@ namespace fc } template - static string to_string( const T& v ) + static string to_string( const T& v, output_formatting format = stringify_large_ints_and_doubles ) { - return to_string( variant(v) ); + return to_string( variant(v), format ); } template - static string to_pretty_string( const T& v ) + static string to_pretty_string( const T& v, output_formatting format = stringify_large_ints_and_doubles ) { - return to_pretty_string( variant(v) ); + return to_pretty_string( variant(v), format ); } template - static void save_to_file( const T& v, const std::string& p, bool pretty = true ) + static void save_to_file( const T& v, const std::string& p, bool pretty = true, output_formatting format = stringify_large_ints_and_doubles ) { save_to_file( variant(v), fc::path(p), pretty ); } diff --git a/include/fc/io/json_relaxed.hpp b/include/fc/io/json_relaxed.hpp index 202fc54..2422db3 100644 --- a/include/fc/io/json_relaxed.hpp +++ b/include/fc/io/json_relaxed.hpp @@ -581,7 +581,7 @@ namespace fc { namespace json_relaxed continue; } if( skip_white_space(in) ) continue; - string key = stringFromStream( in ); + string key = json_relaxed::stringFromStream( in ); skip_white_space(in); if( in.peek() != ':' ) { @@ -589,7 +589,7 @@ namespace fc { namespace json_relaxed ("key", key) ); } in.get(); - auto val = variant_from_stream( in ); + auto val = json_relaxed::variant_from_stream( in ); obj(std::move(key),std::move(val)); skip_white_space(in); @@ -630,7 +630,7 @@ namespace fc { namespace json_relaxed continue; } if( skip_white_space(in) ) continue; - ar.push_back( variant_from_stream(in) ); + ar.push_back( json_relaxed::variant_from_stream(in) ); skip_white_space(in); } if( in.peek() != ']' ) @@ -647,7 +647,7 @@ namespace fc { namespace json_relaxed variant numberFromStream( T& in ) { try { fc::string token = tokenFromStream(in); - variant result = parseNumberOrStr( token ); + variant result = json_relaxed::parseNumberOrStr( token ); if( strict && !(result.is_int64() || result.is_uint64() || result.is_double()) ) FC_THROW_EXCEPTION( parse_error_exception, "expected: number" ); return result; @@ -700,11 +700,11 @@ namespace fc { namespace json_relaxed in.get(); continue; case '"': - return stringFromStream( in ); + return json_relaxed::stringFromStream( in ); case '{': - return objectFromStream( in ); + return json_relaxed::objectFromStream( in ); case '[': - return arrayFromStream( in ); + return json_relaxed::arrayFromStream( in ); case '-': case '+': case '.': @@ -718,7 +718,7 @@ namespace fc { namespace json_relaxed case '7': case '8': case '9': - return numberFromStream( in ); + return json_relaxed::numberFromStream( in ); // null, true, false, or 'warning' / string case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': @@ -729,7 +729,7 @@ namespace fc { namespace json_relaxed case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case '/': - return wordFromStream( in ); + return json_relaxed::wordFromStream( in ); case 0x04: // ^D end of transmission case EOF: FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" ); diff --git a/src/io/json.cpp b/src/io/json.cpp index 2890ba3..84446e1 100644 --- a/src/io/json.cpp +++ b/src/io/json.cpp @@ -15,20 +15,19 @@ namespace fc { // forward declarations of provided functions - template variant variant_from_stream( T& in ); + template variant variant_from_stream( T& in ); template char parseEscape( T& in ); template fc::string stringFromStream( T& in ); template bool skip_white_space( T& in ); template fc::string stringFromToken( T& in ); - template variant_object objectFromStream( T& in ); - template variants arrayFromStream( T& in ); - template variant number_from_stream( T& in ); + template variant_object objectFromStream( T& in ); + template variants arrayFromStream( T& in ); + template variant number_from_stream( T& in ); template variant token_from_stream( T& in ); - template variant variant_from_stream( T& in ); void escape_string( const string& str, ostream& os ); - template void to_stream( T& os, const variants& a ); - template void to_stream( T& os, const variant_object& o ); - template void to_stream( T& os, const variant& v ); + template void to_stream( T& os, const variants& a, json::output_formatting format ); + template void to_stream( T& os, const variant_object& o, json::output_formatting format ); + template void to_stream( T& os, const variant& v, json::output_formatting format ); fc::string pretty_print( const fc::string& v, uint8_t indent ); } @@ -168,7 +167,7 @@ namespace fc ("token", token.str() ) ); } - template + template variant_object objectFromStream( T& in ) { mutable_variant_object obj; @@ -197,7 +196,7 @@ namespace fc ("key", key) ); } in.get(); - auto val = variant_from_stream( in ); + auto val = variant_from_stream( in ); obj(std::move(key),std::move(val)); skip_white_space(in); @@ -219,7 +218,7 @@ namespace fc } FC_RETHROW_EXCEPTIONS( warn, "Error parsing object" ); } - template + template variants arrayFromStream( T& in ) { variants ar; @@ -238,7 +237,7 @@ namespace fc continue; } if( skip_white_space(in) ) continue; - ar.push_back( variant_from_stream(in) ); + ar.push_back( variant_from_stream(in) ); skip_white_space(in); } if( in.peek() != ']' ) @@ -251,7 +250,7 @@ namespace fc return ar; } - template + template variant number_from_stream( T& in ) { fc::stringstream ss; @@ -309,7 +308,7 @@ namespace fc if (str == "-." || str == ".") // check the obviously wrong things we could have encountered FC_THROW_EXCEPTION(parse_error_exception, "Can't parse token \"${token}\" as a JSON numeric constant", ("token", str)); if( dot ) - return to_double(str); + return parser_type == json::legacy_parser_with_string_doubles ? variant(str) : variant(to_double(str)); if( neg ) return to_int64(str); return to_uint64(str); @@ -386,7 +385,7 @@ namespace fc } - template + template variant variant_from_stream( T& in ) { skip_white_space(in); @@ -404,9 +403,9 @@ namespace fc case '"': return stringFromStream( in ); case '{': - return objectFromStream( in ); + return objectFromStream( in ); case '[': - return arrayFromStream( in ); + return arrayFromStream( in ); case '-': case '.': case '0': @@ -419,7 +418,7 @@ namespace fc case '7': case '8': case '9': - return number_from_stream( in ); + return number_from_stream( in ); // null, true, false, or 'warning' / string case 'n': case 't': @@ -466,7 +465,9 @@ namespace fc switch( ptype ) { case legacy_parser: - return variant_from_stream( in ); + return variant_from_stream( in ); + case legacy_parser_with_string_doubles: + return variant_from_stream( in ); case strict_parser: return json_relaxed::variant_from_stream( in ); case relaxed_parser: @@ -548,14 +549,14 @@ namespace fc } template - void to_stream( T& os, const variants& a ) + void to_stream( T& os, const variants& a, json::output_formatting format ) { os << '['; auto itr = a.begin(); while( itr != a.end() ) { - to_stream( os, *itr ); + to_stream( os, *itr, format ); ++itr; if( itr != a.end() ) os << ','; @@ -563,7 +564,7 @@ namespace fc os << ']'; } template - void to_stream( T& os, const variant_object& o ) + void to_stream( T& os, const variant_object& o, json::output_formatting format ) { os << '{'; auto itr = o.begin(); @@ -572,7 +573,7 @@ namespace fc { escape_string( itr->key(), os ); os << ':'; - to_stream( os, itr->value() ); + to_stream( os, itr->value(), format ); ++itr; if( itr != o.end() ) os << ','; @@ -581,7 +582,7 @@ namespace fc } template - void to_stream( T& os, const variant& v ) + void to_stream( T& os, const variant& v, json::output_formatting format ) { switch( v.get_type() ) { @@ -591,24 +592,30 @@ namespace fc case variant::int64_type: { int64_t i = v.as_int64(); - if( i > 0xffffffff ) + if( format == json::stringify_large_ints_and_doubles && + i > 0xffffffff ) os << '"'< 0xffffffff ) + if( format == json::stringify_large_ints_and_doubles && + i > 0xffffffff ) os << '"'<( bi ); + case legacy_parser_with_string_doubles: + return variant_from_stream( bi ); case strict_parser: return json_relaxed::variant_from_stream( bi ); case relaxed_parser: @@ -774,7 +783,9 @@ namespace fc switch( ptype ) { case legacy_parser: - return variant_from_stream( in ); + return variant_from_stream( in ); + case legacy_parser_with_string_doubles: + return variant_from_stream( in ); case strict_parser: return json_relaxed::variant_from_stream( in ); case relaxed_parser: @@ -784,19 +795,19 @@ namespace fc } } - ostream& json::to_stream( ostream& out, const variant& v ) + ostream& json::to_stream( ostream& out, const variant& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) { - fc::to_stream( out, v ); + fc::to_stream( out, v, format ); return out; } - ostream& json::to_stream( ostream& out, const variants& v ) + ostream& json::to_stream( ostream& out, const variants& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) { - fc::to_stream( out, v ); + fc::to_stream( out, v, format ); return out; } - ostream& json::to_stream( ostream& out, const variant_object& v ) + ostream& json::to_stream( ostream& out, const variant_object& v, output_formatting format /* = stringify_large_ints_and_doubles */ ) { - fc::to_stream( out, v ); + fc::to_stream( out, v, format ); return out; } @@ -807,7 +818,10 @@ namespace fc switch( ptype ) { case legacy_parser: - variant_from_stream( in ); + variant_from_stream( in ); + break; + case legacy_parser_with_string_doubles: + variant_from_stream( in ); break; case strict_parser: json_relaxed::variant_from_stream( in ); diff --git a/src/string.cpp b/src/string.cpp index 93b6b7a..0b7cb9f 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -9,6 +9,7 @@ #include #include #include +#include /** * Implemented with std::string for now. @@ -127,11 +128,12 @@ namespace fc { FC_RETHROW_EXCEPTIONS( warn, "${i} => double", ("i",i) ) } - fc::string to_string( double d) + fc::string to_string(double d) { - std::stringstream ss; - ss << std::setprecision(12) << std::fixed << d; - return ss.str(); //boost::lexical_cast(d); + // +2 is required to ensure that the double is rounded correctly when read back in. http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html + std::stringstream ss; + ss << std::setprecision(std::numeric_limits::digits10 + 2) << std::fixed << d; + return ss.str(); } fc::string to_string( uint64_t d)