From ff0a4d51336dcd1c8f0ec7485f8c28245634b641 Mon Sep 17 00:00:00 2001 From: Christophe Le Saec <51320496+clesaec@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:57:42 +0200 Subject: [PATCH] AVRO-2885: better check int vs float value (#2401) --- .../java/org/apache/avro/io/JsonDecoder.java | 30 ++++++++++++++----- .../org/apache/avro/io/TestJsonDecoder.java | 16 ++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lang/java/avro/src/main/java/org/apache/avro/io/JsonDecoder.java b/lang/java/avro/src/main/java/org/apache/avro/io/JsonDecoder.java index c1c38511ab4..2ad496a5b87 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/io/JsonDecoder.java +++ b/lang/java/avro/src/main/java/org/apache/avro/io/JsonDecoder.java @@ -86,7 +86,7 @@ private static Symbol getSymbol(Schema schema) { *

* Otherwise, this JsonDecoder will reset its state and then reconfigure its * input. - * + * * @param in The InputStream to read from. Cannot be null. * @throws IOException * @throws NullPointerException if {@code in} is {@code null} @@ -109,7 +109,7 @@ public JsonDecoder configure(InputStream in) throws IOException { *

* Otherwise, this JsonDecoder will reset its state and then reconfigure its * input. - * + * * @param in The String to read from. Cannot be null. * @throws IOException * @throws NullPointerException if {@code in} is {@code null} @@ -157,25 +157,39 @@ public boolean readBoolean() throws IOException { @Override public int readInt() throws IOException { advance(Symbol.INT); - if (in.getCurrentToken().isNumeric()) { + if (in.getCurrentToken() == JsonToken.VALUE_NUMBER_INT) { int result = in.getIntValue(); in.nextToken(); return result; - } else { - throw error("int"); } + if (in.getCurrentToken() == JsonToken.VALUE_NUMBER_FLOAT) { + float value = in.getFloatValue(); + if (Math.abs(value - Math.round(value)) <= Float.MIN_VALUE) { + int result = Math.round(value); + in.nextToken(); + return result; + } + } + throw error("int"); } @Override public long readLong() throws IOException { advance(Symbol.LONG); - if (in.getCurrentToken().isNumeric()) { + if (in.getCurrentToken() == JsonToken.VALUE_NUMBER_INT) { long result = in.getLongValue(); in.nextToken(); return result; - } else { - throw error("long"); } + if (in.getCurrentToken() == JsonToken.VALUE_NUMBER_FLOAT) { + double value = in.getDoubleValue(); + if (Math.abs(value - Math.round(value)) <= Double.MIN_VALUE) { + long result = Math.round(value); + in.nextToken(); + return result; + } + } + throw error("long"); } @Override diff --git a/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java b/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java index 693fbd421e8..05057139600 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java +++ b/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java @@ -19,11 +19,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import org.apache.avro.AvroTypeException; import org.apache.avro.Schema; +import org.apache.avro.SchemaBuilder; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericRecord; + +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.io.IOException; + public class TestJsonDecoder { @Test @@ -76,4 +82,14 @@ void reorderFields() throws Exception { assertEquals(200, in.readLong()); in.skipArray(); } + + @Test + void testIntWithError() throws IOException { + Schema schema = SchemaBuilder.builder("test").record("example").fields().requiredInt("id").endRecord(); + String record = "{ \"id\": -1.2 }"; + + GenericDatumReader reader = new GenericDatumReader<>(schema, schema); + JsonDecoder decoder = DecoderFactory.get().jsonDecoder(schema, record); + Assertions.assertThrows(AvroTypeException.class, () -> reader.read(null, decoder)); + } }