jakartaee / jsonp-api

Jakarta JSON Processing
https://eclipse.org/ee4j/jsonp
Other
137 stars 59 forks source link

provide "NaN" and "Infinity" when (de)serializing Java Numbers #209

Open nimo23 opened 4 years ago

nimo23 commented 4 years ago

Json-P uses this to (de)serialize Double-values to and from Json:

JsonGenerator write(double value);

Please provide the ability to (de)serialize "NaN", "+Infinity" and "-Infinity" with any type of Numbers (Double, etc) to produce and consume something like this

{
  "val1" : "NaN",
  "val2" : 1.0,
  "val3" : 0.0,
  "val4" : "+Infinity"
  "val5" : "-Infinity"
}

converter to produce json string:

try{
    // normal conversion to double
}
catch(NumberFormatException ex){
    if(Double.isNaN(val)) return "NaN";
    if(Double.isInfinite(val) && val>0) return "Infinity";
    if(Double.isInfinite(val) && val<0) return "-Infinity";
}

converter to consume "NaN" and "Infinity" from Json-String:

try{
    if(val == "NaN") return Double.NaN;
    if(val == "Infinity") return Double.POSITIVE_INFINITY;
    if(val == "-Infinity") return Double.NEGATIVE_INFINITY;
    // normal conversion to double
    ...
}

For example, Json-P could automatically use the Double-(de)serializer, if Double-Objects are used. If Json-P detects primitive double-types, the double-(de)serializer can be used.

Also, Json-P should provide the Json-P configuration property NAN_AS_STRINGS:

// uses strings for "NaN", "+Infinity", "-Infinity"
// instead of throwing NumberFormatException when (de)serializing
Maps.of(JsonGenerator.WRITE_NAN_AS_STRINGS, true)

Actually, Json-P are not able to consume and produce Java Numbers complete: "NaN" and "Infinity" are still valid information about numbers which should not be thrown with exceptions by default.

Related:

aguibert commented 4 years ago

Copying a bit of context over from #160, @leadpony made an important comment here: https://github.com/eclipse-ee4j/jsonp/issues/160#issuecomment-521802692

Which states:

The JSON grammar does not allow NaN and Infinity to be represented and this change may produce JSON which is not interchangeable between various kinds of platforms.

RFC 8259 states:

Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.

The feature can be configured with an option WRITE_NAN_AS_STRINGS in Jackson.

nimo23 commented 4 years ago

Suggestion:

Provide 3 states when dealing with Java Numbers:

1. Config with WRITE_NAN_AS_STRINGS

// uses strings for "NaN", "+Infinity", "-Infinity"
// instead of throwing NumberFormatException when (de)serializing
Maps.of(JsonGenerator.WRITE_NAN_AS_STRINGS, true);

produces/consumes:

{
  "val1" : "NaN",
  "val2" : 1.0,
  "val3" : 0.0,
  "val4" : "+Infinity"
  "val5" : "-Infinity"
}

2. Config with WRITE_NAN_AS_NULLS

// uses NULL for "NaN", "+Infinity", "-Infinity"
// instead of throwing NumberFormatException when (de)serializing
Maps.of(JsonGenerator.WRITE_NAN_AS_NULLS, true);

produces/consumes:

{
  "val1" : null,
  "val2" : 1.0,
  "val3" : 0.0,
  "val4" : null
  "val5" : null
}

3. If both are true

// uses "NaN", "+Infinity", "-Infinity"-Strings for Number Wrapper types (Double,..)
// uses NULL for primitive types (double,..)
// instead of throwing NumberFormatException when (de)serializing
Maps.of(
JsonGenerator.WRITE_NAN_AS_STRINGS, true,
JsonGenerator.WRITE_NAN_AS_NULLS, true);

3. If both settings are false throws NumberFormatException when (de)serializing "NaN", "+Infinity", "-Infinity".

To sum up: The default should be at least JsonGenerator.WRITE_NAN_AS_NULLS, true (RFC-compliant) instead of throwing exception. The default with Number Wrapper types could be JsonGenerator.WRITE_NAN_AS_STRINGS, true.

jbescos commented 2 years ago

I am implementing this in Parsson.

In the point 3 I have a question. When both are true, the logic is split in 2 ways:

// uses "NaN", "+Infinity", "-Infinity"-Strings for Number Wrapper types (Double,..)
// uses NULL for primitive types (double,..)

The first one 'uses "NaN", "+Infinity", "-Infinity"-Strings for Number Wrapper types (Double,..)'over complicates the logic a lot, because internally it works with primitive types.

Recently a new method was added in JsonValue to manage Numbers here https://github.com/eclipse-ee4j/jsonp/pull/302 but by default it delegates in existing methods that works primitive types.

What is the motivation to make a split between primitive and not primitive types when both are true?

jbescos commented 2 years ago