Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVRO-2032: [Java] Add support for NaN, Infinity and -Infinity in JsonDecoder #3066

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lang/java/avro/src/main/java/org/apache/avro/io/JsonDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,19 @@ public float readFloat() throws IOException {
float result = in.getFloatValue();
in.nextToken();
return result;
} else if (in.getCurrentToken() == JsonToken.VALUE_STRING) {
String stringValue = in.getText();
in.nextToken();
if (isNaNString(stringValue)) {
return Float.NaN;
}
if (isNegativeInfinityString(stringValue)) {
return Float.NEGATIVE_INFINITY;
}
if (isPositiveInfinityString(stringValue)) {
return Float.POSITIVE_INFINITY;
}
throw error("float");
} else {
throw error("float");
}
Expand All @@ -211,11 +224,42 @@ public double readDouble() throws IOException {
double result = in.getDoubleValue();
in.nextToken();
return result;
} else if (in.getCurrentToken() == JsonToken.VALUE_STRING) {
String stringValue = in.getText();
in.nextToken();
if (isNaNString(stringValue)) {
return Double.NaN;
}
if (isNegativeInfinityString(stringValue)) {
return Double.NEGATIVE_INFINITY;
}
if (isPositiveInfinityString(stringValue)) {
return Double.POSITIVE_INFINITY;
}
throw error("double");
} else {
throw error("double");
}
}

// check whether the given string represents an IEEE 754 'NaN' string value as
// serialized by Jackson
private static boolean isNaNString(String value) {
return "NaN".equals(value);
}

// check whether the given string represents an IEEE 754 'Infinity' string value
// as serialized by Jackson
private static boolean isPositiveInfinityString(String value) {
return "Infinity".equals(value) || "INF".equals(value);
}

// check whether the given string represents an IEEE 754 '-Infinity' string
// value as serialized by Jackson
private static boolean isNegativeInfinityString(String value) {
return "-Infinity".equals(value) || "-INF".equals(value);
}

@Override
public Utf8 readString(Utf8 old) throws IOException {
return new Utf8(readString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,26 @@ void testIntWithError() throws IOException {
JsonDecoder decoder = DecoderFactory.get().jsonDecoder(schema, record);
Assertions.assertThrows(AvroTypeException.class, () -> reader.read(null, decoder));
}

@Test
void testIeee754SpecialCases() throws IOException {
String def = "{\"type\":\"record\",\"name\":\"X\",\"fields\": [" + "{\"type\":\"float\",\"name\":\"nanFloat\"},"
+ "{\"type\":\"float\",\"name\":\"infinityFloat\"},"
+ "{\"type\":\"float\",\"name\":\"negativeInfinityFloat\"}," + "{\"type\":\"double\",\"name\":\"nanDouble\"},"
+ "{\"type\":\"double\",\"name\":\"infinityDouble\"},"
+ "{\"type\":\"double\",\"name\":\"negativeInfinityDouble\"}" + "]}";
Schema schema = new Schema.Parser().parse(def);
DatumReader<GenericRecord> reader = new GenericDatumReader<>(schema);

String record = "{\"nanFloat\":\"NaN\", \"infinityFloat\":\"Infinity\", \"negativeInfinityFloat\":\"-Infinity\", "
+ "\"nanDouble\":\"NaN\", \"infinityDouble\":\"Infinity\", \"negativeInfinityDouble\":\"-Infinity\"}";
Decoder decoder = DecoderFactory.get().jsonDecoder(schema, record);
GenericRecord r = reader.read(null, decoder);
assertEquals(Float.NaN, r.get("nanFloat"));
assertEquals(Float.POSITIVE_INFINITY, r.get("infinityFloat"));
assertEquals(Float.NEGATIVE_INFINITY, r.get("negativeInfinityFloat"));
assertEquals(Double.NaN, r.get("nanDouble"));
assertEquals(Double.POSITIVE_INFINITY, r.get("infinityDouble"));
assertEquals(Double.NEGATIVE_INFINITY, r.get("negativeInfinityDouble"));
}
}
Loading