mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 22:21:09 +03:00
JSON: option to decode special strings as floats.
Passing JsonSpecialFloats.yes to parseJSON prompts the parser to interpret "NaN", "Infinite", and "-Infinite" as the special floating-point values they represent. Adds the overload parseJSON(JSONValue, SpecialFloats) that takes a JSONValue and the JsonSpecialFloats flag so users do not have to pass maxDepth in order to specify special float handling behavior.
This commit is contained in:
parent
ef0b39334d
commit
c33dca96be
1 changed files with 73 additions and 12 deletions
85
std/json.d
85
std/json.d
|
@ -72,7 +72,7 @@ enum JSONFloatLiteral : string
|
|||
/**
|
||||
Flag that enables encoding special float values (NaN/Inf) as strings.
|
||||
*/
|
||||
alias SpecialFloats = Flag!"SpecialFloats";
|
||||
alias JsonSpecialFloats = Flag!"JsonSpecialFloats";
|
||||
|
||||
/**
|
||||
JSON type enumeration
|
||||
|
@ -612,7 +612,7 @@ struct JSONValue
|
|||
|
||||
/// Implicitly calls $(D toJSON) on this JSONValue.
|
||||
/// $(I specialFloats) will enable encoding NaN/Inf as strings.
|
||||
string toString(in SpecialFloats specialFloats = SpecialFloats.no) const
|
||||
string toString(in JsonSpecialFloats specialFloats = JsonSpecialFloats.no) const
|
||||
{
|
||||
return toJSON(&this, false, specialFloats);
|
||||
}
|
||||
|
@ -620,7 +620,7 @@ struct JSONValue
|
|||
/// Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but
|
||||
/// also passes $(I true) as $(I pretty) argument.
|
||||
/// $(I specialFloats) will enable encoding NaN/Inf as strings.
|
||||
string toPrettyString(in SpecialFloats specialFloats = SpecialFloats.no) const
|
||||
string toPrettyString(in JsonSpecialFloats specialFloats = JsonSpecialFloats.no) const
|
||||
{
|
||||
return toJSON(&this, true, specialFloats);
|
||||
}
|
||||
|
@ -629,7 +629,8 @@ struct JSONValue
|
|||
/**
|
||||
Parses a serialized string and returns a tree of JSON values.
|
||||
*/
|
||||
JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T)
|
||||
JSONValue parseJSON(T)(T json, int maxDepth = -1, JsonSpecialFloats specialFloats = JsonSpecialFloats.no)
|
||||
if(isInputRange!T)
|
||||
{
|
||||
import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
|
||||
import std.utf : toUTF8;
|
||||
|
@ -769,6 +770,22 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T)
|
|||
return str.data.length ? str.data : "";
|
||||
}
|
||||
|
||||
bool tryGetSpecialFloat(string str, out double val) {
|
||||
switch(str) {
|
||||
case JSONFloatLiteral.nan:
|
||||
val = double.nan;
|
||||
return true;
|
||||
case JSONFloatLiteral.inf:
|
||||
val = double.infinity;
|
||||
return true;
|
||||
case JSONFloatLiteral.negativeInf:
|
||||
val = -double.infinity;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void parseValue(JSONValue* value)
|
||||
{
|
||||
depth++;
|
||||
|
@ -822,8 +839,17 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T)
|
|||
break;
|
||||
|
||||
case '"':
|
||||
auto str = parseString();
|
||||
|
||||
// if special float parsing is enabled, check if string represents NaN/Inf
|
||||
if (specialFloats && tryGetSpecialFloat(str, value.store.floating)) {
|
||||
// found a special float, its value was placed in value.store.floating
|
||||
value.type_tag = JSON_TYPE.FLOAT;
|
||||
break;
|
||||
}
|
||||
|
||||
value.type_tag = JSON_TYPE.STRING;
|
||||
value.store.str = parseString();
|
||||
value.store.str = str;
|
||||
break;
|
||||
|
||||
case '0': .. case '9':
|
||||
|
@ -923,6 +949,15 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T)
|
|||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
Parses a serialized string and returns a tree of JSON values.
|
||||
*/
|
||||
JSONValue parseJSON(T)(T json, JsonSpecialFloats specialFloats)
|
||||
if(isInputRange!T)
|
||||
{
|
||||
return parseJSON!T(json, -1, specialFloats);
|
||||
}
|
||||
|
||||
/**
|
||||
Takes a tree of JSON values and returns the serialized string.
|
||||
|
||||
|
@ -930,9 +965,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 true serialized string is formatted to be human-readable.
|
||||
If $(D specialFloats) is SpecialFloats.yes, encode special floats (NaN/Infinity) as strings.
|
||||
If $(D specialFloats) is JsonSpecialFloats.yes, encode special floats (NaN/Infinity) as strings.
|
||||
*/
|
||||
string toJSON(in JSONValue* root, in bool pretty = false, in SpecialFloats specialFloats = SpecialFloats.no)
|
||||
string toJSON(in JSONValue* root, in bool pretty = false, in JsonSpecialFloats specialFloats = JsonSpecialFloats.no)
|
||||
{
|
||||
auto json = appender!string();
|
||||
|
||||
|
@ -1444,15 +1479,41 @@ EOF";
|
|||
// handling of special float values (NaN, Inf, -Inf)
|
||||
unittest
|
||||
{
|
||||
import std.math : isNaN, isInfinity;
|
||||
import std.exception : assertThrown;
|
||||
|
||||
// when passing SpecialFloats.yes, encode NaN/Inf as strings
|
||||
assert(JSONValue(float.nan).toString(SpecialFloats.yes) == q{"NaN"});
|
||||
assert(JSONValue(double.infinity).toString(SpecialFloats.yes) == q{"Infinite"});
|
||||
assert(JSONValue(-real.infinity).toString(SpecialFloats.yes) == q{"-Infinite"});
|
||||
// expected representations of NaN and Inf
|
||||
enum {
|
||||
nanString = '"' ~ JSONFloatLiteral.nan ~ '"',
|
||||
infString = '"' ~ JSONFloatLiteral.inf ~ '"',
|
||||
negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"',
|
||||
}
|
||||
|
||||
// when passing SpecialFloats.no, thow on converting NaN/Inf
|
||||
// when passing JsonSpecialFloats.yes, encode NaN/Inf as strings
|
||||
assert(JSONValue(float.nan).toString(JsonSpecialFloats.yes) == nanString);
|
||||
assert(JSONValue(double.infinity).toString(JsonSpecialFloats.yes) == infString);
|
||||
assert(JSONValue(-real.infinity).toString(JsonSpecialFloats.yes) == negativeInfString);
|
||||
|
||||
// when passing JsonSpecialFloats.no, thow on converting NaN/Inf
|
||||
assertThrown!JSONException(JSONValue(float.nan).toString);
|
||||
assertThrown!JSONException(JSONValue(double.infinity).toString);
|
||||
assertThrown!JSONException(JSONValue(-real.infinity).toString);
|
||||
|
||||
// when parsing json with JsonSpecialFloats.yes, decode special strings as floats
|
||||
JSONValue jvNan = parseJSON(nanString, JsonSpecialFloats.yes);
|
||||
JSONValue jvInf = parseJSON(infString, JsonSpecialFloats.yes);
|
||||
JSONValue jvNegInf = parseJSON(negativeInfString, JsonSpecialFloats.yes);
|
||||
|
||||
assert(jvNan.floating.isNaN);
|
||||
assert(jvInf.floating.isInfinity && jvInf.floating > 0);
|
||||
assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0);
|
||||
|
||||
// when parsing json with JsonSpecialFloats.no, decode special strings as strings
|
||||
jvNan = parseJSON(nanString);
|
||||
jvInf = parseJSON(infString);
|
||||
jvNegInf = parseJSON(negativeInfString);
|
||||
|
||||
assert(jvNan.str == JSONFloatLiteral.nan);
|
||||
assert(jvInf.str == JSONFloatLiteral.inf);
|
||||
assert(jvNegInf.str == JSONFloatLiteral.negativeInf);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue