mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
JSON: Nan/Inf code review tweaks.
Replace Flag!"JsonSpecialFloats" with a flagset enum to allow future extensibility. Add more documentation to modified functions.
This commit is contained in:
parent
c33dca96be
commit
fecf9b3972
1 changed files with 42 additions and 28 deletions
70
std/json.d
70
std/json.d
|
@ -70,9 +70,12 @@ enum JSONFloatLiteral : string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Flag that enables encoding special float values (NaN/Inf) as strings.
|
Flags that control how json is encoded and parsed.
|
||||||
*/
|
*/
|
||||||
alias JsonSpecialFloats = Flag!"JsonSpecialFloats";
|
enum JsonOptions {
|
||||||
|
none, /// standard parsing
|
||||||
|
specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
JSON type enumeration
|
JSON type enumeration
|
||||||
|
@ -611,25 +614,30 @@ struct JSONValue
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implicitly calls $(D toJSON) on this JSONValue.
|
/// Implicitly calls $(D toJSON) on this JSONValue.
|
||||||
/// $(I specialFloats) will enable encoding NaN/Inf as strings.
|
/// $(I options) can be used to tweak the conversion behavior.
|
||||||
string toString(in JsonSpecialFloats specialFloats = JsonSpecialFloats.no) const
|
string toString(in JsonOptions options = JsonOptions.none) const
|
||||||
{
|
{
|
||||||
return toJSON(&this, false, specialFloats);
|
return toJSON(&this, false, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but
|
/// Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but
|
||||||
/// also passes $(I true) as $(I pretty) argument.
|
/// also passes $(I true) as $(I pretty) argument.
|
||||||
/// $(I specialFloats) will enable encoding NaN/Inf as strings.
|
/// $(I options) can be used to tweak the conversion behavior
|
||||||
string toPrettyString(in JsonSpecialFloats specialFloats = JsonSpecialFloats.no) const
|
string toPrettyString(in JsonOptions options = JsonOptions.none) const
|
||||||
{
|
{
|
||||||
return toJSON(&this, true, specialFloats);
|
return toJSON(&this, true, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Parses a serialized string and returns a tree of JSON values.
|
Parses a serialized string and returns a tree of JSON values.
|
||||||
|
Throws a $(XREF json,JSONException) if the depth exceeds the max depth.
|
||||||
|
Params:
|
||||||
|
json = json-formatted string to parse
|
||||||
|
maxDepth = maximum depth of nesting allowed, -1 disables depth checking
|
||||||
|
options = enable decoding string representations of NaN/Inf as float values
|
||||||
*/
|
*/
|
||||||
JSONValue parseJSON(T)(T json, int maxDepth = -1, JsonSpecialFloats specialFloats = JsonSpecialFloats.no)
|
JSONValue parseJSON(T)(T json, int maxDepth = -1, JsonOptions options = JsonOptions.none)
|
||||||
if(isInputRange!T)
|
if(isInputRange!T)
|
||||||
{
|
{
|
||||||
import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
|
import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
|
||||||
|
@ -842,7 +850,9 @@ if(isInputRange!T)
|
||||||
auto str = parseString();
|
auto str = parseString();
|
||||||
|
|
||||||
// if special float parsing is enabled, check if string represents NaN/Inf
|
// if special float parsing is enabled, check if string represents NaN/Inf
|
||||||
if (specialFloats && tryGetSpecialFloat(str, value.store.floating)) {
|
if ((options & JsonOptions.specialFloatLiterals) &&
|
||||||
|
tryGetSpecialFloat(str, value.store.floating))
|
||||||
|
{
|
||||||
// found a special float, its value was placed in value.store.floating
|
// found a special float, its value was placed in value.store.floating
|
||||||
value.type_tag = JSON_TYPE.FLOAT;
|
value.type_tag = JSON_TYPE.FLOAT;
|
||||||
break;
|
break;
|
||||||
|
@ -951,11 +961,15 @@ if(isInputRange!T)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Parses a serialized string and returns a tree of JSON values.
|
Parses a serialized string and returns a tree of JSON values.
|
||||||
|
Throws a $(XREF json,JSONException) if the depth exceeds the max depth.
|
||||||
|
Params:
|
||||||
|
json = json-formatted string to parse
|
||||||
|
options = enable decoding string representations of NaN/Inf as float values
|
||||||
*/
|
*/
|
||||||
JSONValue parseJSON(T)(T json, JsonSpecialFloats specialFloats)
|
JSONValue parseJSON(T)(T json, JsonOptions options)
|
||||||
if(isInputRange!T)
|
if(isInputRange!T)
|
||||||
{
|
{
|
||||||
return parseJSON!T(json, -1, specialFloats);
|
return parseJSON!T(json, -1, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -965,9 +979,9 @@ Any Object types will be serialized in a key-sorted order.
|
||||||
|
|
||||||
If $(D pretty) is false no whitespaces are generated.
|
If $(D pretty) is false no whitespaces are generated.
|
||||||
If $(D pretty) is true serialized string is formatted to be human-readable.
|
If $(D pretty) is true serialized string is formatted to be human-readable.
|
||||||
If $(D specialFloats) is JsonSpecialFloats.yes, encode special floats (NaN/Infinity) as strings.
|
Set the $(specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings.
|
||||||
*/
|
*/
|
||||||
string toJSON(in JSONValue* root, in bool pretty = false, in JsonSpecialFloats specialFloats = JsonSpecialFloats.no)
|
string toJSON(in JSONValue* root, in bool pretty = false, in JsonOptions options = JsonOptions.none)
|
||||||
{
|
{
|
||||||
auto json = appender!string();
|
auto json = appender!string();
|
||||||
|
|
||||||
|
@ -1094,21 +1108,21 @@ string toJSON(in JSONValue* root, in bool pretty = false, in JsonSpecialFloats s
|
||||||
auto val = value.store.floating;
|
auto val = value.store.floating;
|
||||||
|
|
||||||
if (val.isNaN) {
|
if (val.isNaN) {
|
||||||
if (specialFloats) {
|
if (options & JsonOptions.specialFloatLiterals) {
|
||||||
toString(JSONFloatLiteral.nan);
|
toString(JSONFloatLiteral.nan);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new JSONException(
|
throw new JSONException(
|
||||||
"Cannot encode NaN. Consider passing the specialFloats flag.");
|
"Cannot encode NaN. Consider passing the specialFloatLiterals flag.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (val.isInfinity) {
|
else if (val.isInfinity) {
|
||||||
if (specialFloats) {
|
if (options & JsonOptions.specialFloatLiterals) {
|
||||||
toString((val > 0) ? JSONFloatLiteral.inf : JSONFloatLiteral.negativeInf);
|
toString((val > 0) ? JSONFloatLiteral.inf : JSONFloatLiteral.negativeInf);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new JSONException(
|
throw new JSONException(
|
||||||
"Cannot encode Infinity. Consider passing the specialFloats flag.");
|
"Cannot encode Infinity. Consider passing the specialFloatLiterals flag.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1489,26 +1503,26 @@ unittest
|
||||||
negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"',
|
negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"',
|
||||||
}
|
}
|
||||||
|
|
||||||
// when passing JsonSpecialFloats.yes, encode NaN/Inf as strings
|
// with the specialFloatLiterals option, encode NaN/Inf as strings
|
||||||
assert(JSONValue(float.nan).toString(JsonSpecialFloats.yes) == nanString);
|
assert(JSONValue(float.nan).toString(JsonOptions.specialFloatLiterals) == nanString);
|
||||||
assert(JSONValue(double.infinity).toString(JsonSpecialFloats.yes) == infString);
|
assert(JSONValue(double.infinity).toString(JsonOptions.specialFloatLiterals) == infString);
|
||||||
assert(JSONValue(-real.infinity).toString(JsonSpecialFloats.yes) == negativeInfString);
|
assert(JSONValue(-real.infinity).toString(JsonOptions.specialFloatLiterals) == negativeInfString);
|
||||||
|
|
||||||
// when passing JsonSpecialFloats.no, thow on converting NaN/Inf
|
// without the specialFloatLiterals option, throw on encoding NaN/Inf
|
||||||
assertThrown!JSONException(JSONValue(float.nan).toString);
|
assertThrown!JSONException(JSONValue(float.nan).toString);
|
||||||
assertThrown!JSONException(JSONValue(double.infinity).toString);
|
assertThrown!JSONException(JSONValue(double.infinity).toString);
|
||||||
assertThrown!JSONException(JSONValue(-real.infinity).toString);
|
assertThrown!JSONException(JSONValue(-real.infinity).toString);
|
||||||
|
|
||||||
// when parsing json with JsonSpecialFloats.yes, decode special strings as floats
|
// when parsing json with specialFloatLiterals option, decode special strings as floats
|
||||||
JSONValue jvNan = parseJSON(nanString, JsonSpecialFloats.yes);
|
JSONValue jvNan = parseJSON(nanString, JsonOptions.specialFloatLiterals);
|
||||||
JSONValue jvInf = parseJSON(infString, JsonSpecialFloats.yes);
|
JSONValue jvInf = parseJSON(infString, JsonOptions.specialFloatLiterals);
|
||||||
JSONValue jvNegInf = parseJSON(negativeInfString, JsonSpecialFloats.yes);
|
JSONValue jvNegInf = parseJSON(negativeInfString, JsonOptions.specialFloatLiterals);
|
||||||
|
|
||||||
assert(jvNan.floating.isNaN);
|
assert(jvNan.floating.isNaN);
|
||||||
assert(jvInf.floating.isInfinity && jvInf.floating > 0);
|
assert(jvInf.floating.isInfinity && jvInf.floating > 0);
|
||||||
assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0);
|
assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0);
|
||||||
|
|
||||||
// when parsing json with JsonSpecialFloats.no, decode special strings as strings
|
// when parsing json without the specialFloatLiterals option, decode special strings as strings
|
||||||
jvNan = parseJSON(nanString);
|
jvNan = parseJSON(nanString);
|
||||||
jvInf = parseJSON(infString);
|
jvInf = parseJSON(infString);
|
||||||
jvNegInf = parseJSON(negativeInfString);
|
jvNegInf = parseJSON(negativeInfString);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue