From 6fb591c2d34ea2acb395ce7c2d7ef790dd6052e9 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Fri, 4 Apr 2025 16:00:32 +0530 Subject: [PATCH 1/6] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectMapper.java | 28 +++++++++++++++++++ .../jackson/databind/ObjectMapperTest.java | 10 +++++++ 2 files changed, 38 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 140872e85f..4ce22efe6a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3855,6 +3855,34 @@ public T readValue(String content, Class valueType) return readValue(content, _typeFactory.constructType(valueType)); } + /** + * Utility method to get the class type from a varargs array. + * + * @param the generic type + * @param array the varargs array + * @return the class of the array component + */ + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + + /** + * Reads the value from the given JSON content and automatically detects the class type. + * + * @param the type of the object to read + * @param content the JSON string + * @param reified don't pass any values here. It's a trick to detect the class type. + * @return the deserialized object + * @throws JsonProcessingException if there is a problem processing the JSON + */ + public T readValue(String content, T... reified) throws JsonProcessingException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(content, _typeFactory.constructType(getClassOf(reified))); + } + /** * Method to deserialize JSON content from given JSON content String. * diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java index a249682b76..ef5198e84a 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java @@ -385,6 +385,16 @@ public void testJsonFactoryLinkage() assertSame(m, f.getCodec()); } + @Test + public void testAutoDetectClasses() throws Exception + { + ObjectMapper m = new ObjectMapper(); + final String JSON = "{ \"x\" : 3 }"; + + Bean bean = m.readValue(JSON); + assertNotNull(bean); + } + @Test public void testProviderConfig() throws Exception { From d885656616a5dda4e8fa981b42c982e8db6210db Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sun, 6 Apr 2025 09:04:57 +0530 Subject: [PATCH 2/6] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectMapper.java | 26 +++++++++++ .../databind/BoundsChecksForInputTest.java | 3 ++ .../databind/deser/JacksonTypesDeserTest.java | 44 +++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 4ce22efe6a..16a6dec995 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -3055,6 +3055,23 @@ public T readValue(JsonParser p, Class valueType) return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType)); } + /** + * Reads the value from the given JSON content and automatically detects the class type. + * + * @param the type of the object to read + * @param p the JsonParser + * @param reified don't pass any values here. It's a trick to detect the class type. + * @return the deserialized object + * @throws JsonProcessingException if there is a problem processing the JSON + */ + public T readValue(JsonParser p, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(p, _typeFactory.constructType(getClassOf(reified))); + } + /** * Method to deserialize JSON content into a Java type, reference * to which is passed as argument. Type is passed using so-called @@ -3985,6 +4002,15 @@ public T readValue(byte[] src, int offset, int len, return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType)); } + @SuppressWarnings("unchecked") + public T readValue(byte[] src, int offset, int len, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(src, offset, len, _typeFactory.constructType(getClassOf(reified))); + } + @SuppressWarnings({ "unchecked" }) public T readValue(byte[] src, TypeReference valueTypeRef) throws IOException, StreamReadException, DatabindException diff --git a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java index 493fd4c482..89a0f11d85 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java @@ -45,6 +45,9 @@ public void testBoundsWithByteArrayInput() throws Exception { final JavaType TYPE = MAPPER.constructType(String.class); _testBoundsWithByteArrayInput( (data,offset,len)->MAPPER.readValue(data, offset, len, TYPE)); + + _testBoundsWithByteArrayInput( + (data,offset,len)->MAPPER.readValue(data, offset, len)); } private void _testBoundsWithByteArrayInput(ByteBackedCreation creator) throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java index 922cd34a04..a55440f1d9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/JacksonTypesDeserTest.java @@ -82,6 +82,50 @@ public void testTokenBufferWithSample() throws Exception } } + @Test + public void testTokenBufferWithSequenceWithAutoDetectClass() throws Exception + { + final ObjectMapper mapper = jsonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); + + // and then sequence of other things + JsonParser p = mapper.createParser("[ 32, [ 1 ], \"abc\", { \"a\" : true } ]"); + assertToken(JsonToken.START_ARRAY, p.nextToken()); + + assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); + TokenBuffer buf = mapper.readValue(p); + + // check manually... + JsonParser bufParser = buf.asParser(); + assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); + assertEquals(32, bufParser.getIntValue()); + assertNull(bufParser.nextToken()); + + // then bind to another + buf = mapper.readValue(p); + bufParser = buf.asParser(); + assertToken(JsonToken.START_ARRAY, bufParser.nextToken()); + assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken()); + assertEquals(1, bufParser.getIntValue()); + assertToken(JsonToken.END_ARRAY, bufParser.nextToken()); + assertNull(bufParser.nextToken()); + + // third one, with automatic binding + buf = mapper.readValue(p); + String str = mapper.readValue(buf.asParser()); + assertEquals("abc", str); + + // and ditto for last one + buf = mapper.readValue(p, TokenBuffer.class); + Map map = mapper.readValue(buf.asParser()); + assertEquals(1, map.size()); + assertEquals(Boolean.TRUE, map.get("a")); + + assertEquals(JsonToken.END_ARRAY, p.nextToken()); + assertNull(p.nextToken()); + } + @SuppressWarnings("resource") @Test public void testTokenBufferWithSequence() throws Exception From 013b19a1726ff44c634cf74f3b7f009039852b2d Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sun, 6 Apr 2025 12:25:57 +0530 Subject: [PATCH 3/6] Automatically detect class to deserialize #5064 --- .../jackson/databind/ObjectReader.java | 28 ++++++++++++++++++- .../jackson/databind/ObjectReaderTest.java | 3 ++ .../databind/deser/jdk/LocaleDeserTest.java | 9 ++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index fa45a4029c..4dec0a2131 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -1492,7 +1492,7 @@ public void writeTree(JsonGenerator g, TreeNode rootNode) { * @param src Source to read content from */ @SuppressWarnings("unchecked") - public T readValue(InputStream src) throws IOException + private T readValue(InputStream src) throws IOException { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); @@ -1515,6 +1515,12 @@ public T readValue(InputStream src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } + @SuppressWarnings("unchecked") + public T readValue(InputStream src, T... reified) throws IOException + { + return forType(getClassOf(reified)).readValue(src); + } + /** * Method that binds content read from given input source, * using configuration of this reader. @@ -1585,6 +1591,26 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } + @SuppressWarnings("unchecked") + public T readValue(String src, T... reified) throws IOException { + if (reified.length > 0) { + throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); + } + + return readValue(src, getClassOf(reified)); + } + + /** + * Utility method to get the class type from a varargs array. + * + * @param the generic type + * @param array the varargs array + * @return the class of the array component + */ + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + /** * Method that binds content read from given byte array, * using configuration of this reader. diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index 5c5c16931d..987c8a5577 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -454,6 +454,9 @@ public void testCanPassResultToOverloadedMethod() throws Exception { ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller"); process(reader.readValue(source, POJO.class)); + + POJO pojo = reader.readValue(source); + process(pojo); } void process(POJO pojo) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java index a65bc137a7..71048da278 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java @@ -268,6 +268,15 @@ public void testLocaleFuzz47034() throws Exception assertNotNull(loc); } + @Test + public void testLocaleFuzz47034WithAutoDetectClss() throws Exception + { + Locale loc = MAPPER.reader() + .without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .readValue(getClass().getResourceAsStream("/fuzz/oss-fuzz-47034.json")); + assertNotNull(loc); + } + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47036 // @since 2.14 @Test From 9da0c3565bc1967a1f4bbb79dccb3ee7016ef189 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Mon, 7 Apr 2025 13:31:26 +0530 Subject: [PATCH 4/6] Automatically detect class to deserialize #5064 --- .../fasterxml/jackson/databind/ObjectReader.java | 16 +++++++++++++++- .../jackson/databind/ObjectReaderTest.java | 7 +++++++ .../databind/deser/jdk/LocaleDeserTest.java | 9 --------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 4dec0a2131..b2c7245ea5 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -777,6 +777,20 @@ public ObjectReader forType(JavaType valueType) _valueToUpdate, _schema, _injectableValues, det); } + /** + * Method for constructing a new reader instance. + */ + public ObjectReader readerFor(T... reified) { + // Detect class type + Class clazz = getClassOf(reified); + + // Convert class to JavaType + JavaType valueType = _config.getTypeFactory().constructType(clazz); + + return forType(valueType); + } + + /** * Method for constructing a new reader instance that is configured * to data bind into specified type. @@ -1492,7 +1506,7 @@ public void writeTree(JsonGenerator g, TreeNode rootNode) { * @param src Source to read content from */ @SuppressWarnings("unchecked") - private T readValue(InputStream src) throws IOException + public T readValue(InputStream src) throws IOException { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index 987c8a5577..edfbe8e659 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -291,6 +291,13 @@ public void testDeprecatedSettings() throws Exception assertSame(newR, newR.withRootName(PropertyName.construct("foo"))); } + @Test + public void testAutoDetectForReader() throws Exception + { + Number n = MAPPER.reader().readerFor().readValue("123 "); + assertEquals(Integer.valueOf(123), n); + } + @Test public void testNoPrefetch() throws Exception { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java index 71048da278..a65bc137a7 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/LocaleDeserTest.java @@ -268,15 +268,6 @@ public void testLocaleFuzz47034() throws Exception assertNotNull(loc); } - @Test - public void testLocaleFuzz47034WithAutoDetectClss() throws Exception - { - Locale loc = MAPPER.reader() - .without(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) - .readValue(getClass().getResourceAsStream("/fuzz/oss-fuzz-47034.json")); - assertNotNull(loc); - } - // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47036 // @since 2.14 @Test From 73dc9e5b44457decd4bd6bc93ad39ed5ba244e85 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sat, 12 Apr 2025 09:56:10 +0530 Subject: [PATCH 5/6] Review comments fixes. Removed Test cases of removed code. #5064 --- .../fasterxml/jackson/databind/ObjectReader.java | 15 --------------- .../jackson/databind/ObjectReaderTest.java | 3 --- 2 files changed, 18 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index b2c7245ea5..00850145a1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -1529,12 +1529,6 @@ public T readValue(InputStream src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - @SuppressWarnings("unchecked") - public T readValue(InputStream src, T... reified) throws IOException - { - return forType(getClassOf(reified)).readValue(src); - } - /** * Method that binds content read from given input source, * using configuration of this reader. @@ -1605,15 +1599,6 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - @SuppressWarnings("unchecked") - public T readValue(String src, T... reified) throws IOException { - if (reified.length > 0) { - throw new IllegalArgumentException("Please don't pass any values here. Java will detect class automatically."); - } - - return readValue(src, getClassOf(reified)); - } - /** * Utility method to get the class type from a varargs array. * diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index edfbe8e659..b7fe167184 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -461,9 +461,6 @@ public void testCanPassResultToOverloadedMethod() throws Exception { ObjectReader reader = MAPPER.readerFor(POJO.class).at("/foo/bar/caller"); process(reader.readValue(source, POJO.class)); - - POJO pojo = reader.readValue(source); - process(pojo); } void process(POJO pojo) { From f018ed429ad8230b741f026bf181ef6df4f67a5f Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Sat, 12 Apr 2025 10:03:34 +0530 Subject: [PATCH 6/6] Review comments fixes. Removed Test cases of removed code. #5064 --- .../jackson/databind/ObjectReader.java | 25 ------------------- .../databind/BoundsChecksForInputTest.java | 3 --- .../jackson/databind/ObjectReaderTest.java | 7 ------ 3 files changed, 35 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java index 00850145a1..fa45a4029c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java @@ -777,20 +777,6 @@ public ObjectReader forType(JavaType valueType) _valueToUpdate, _schema, _injectableValues, det); } - /** - * Method for constructing a new reader instance. - */ - public ObjectReader readerFor(T... reified) { - // Detect class type - Class clazz = getClassOf(reified); - - // Convert class to JavaType - JavaType valueType = _config.getTypeFactory().constructType(clazz); - - return forType(valueType); - } - - /** * Method for constructing a new reader instance that is configured * to data bind into specified type. @@ -1599,17 +1585,6 @@ public T readValue(String src, Class valueType) throws IOException return (T) forType(valueType).readValue(src); } - /** - * Utility method to get the class type from a varargs array. - * - * @param the generic type - * @param array the varargs array - * @return the class of the array component - */ - private static Class getClassOf(T[] array) { - return (Class) array.getClass().getComponentType(); - } - /** * Method that binds content read from given byte array, * using configuration of this reader. diff --git a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java index 89a0f11d85..493fd4c482 100644 --- a/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/BoundsChecksForInputTest.java @@ -45,9 +45,6 @@ public void testBoundsWithByteArrayInput() throws Exception { final JavaType TYPE = MAPPER.constructType(String.class); _testBoundsWithByteArrayInput( (data,offset,len)->MAPPER.readValue(data, offset, len, TYPE)); - - _testBoundsWithByteArrayInput( - (data,offset,len)->MAPPER.readValue(data, offset, len)); } private void _testBoundsWithByteArrayInput(ByteBackedCreation creator) throws Exception diff --git a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java index b7fe167184..5c5c16931d 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ObjectReaderTest.java @@ -291,13 +291,6 @@ public void testDeprecatedSettings() throws Exception assertSame(newR, newR.withRootName(PropertyName.construct("foo"))); } - @Test - public void testAutoDetectForReader() throws Exception - { - Number n = MAPPER.reader().readerFor().readValue("123 "); - assertEquals(Integer.valueOf(123), n); - } - @Test public void testNoPrefetch() throws Exception {