From 9e60d1bbda47fadf7aa80c665963e8adfe5b41fd Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Tue, 9 Sep 2025 15:13:43 +0530 Subject: [PATCH 1/6] Renamed method to readObject and taking base from 3.x --- .../tools/jackson/databind/ObjectMapper.java | 45 +++++++++++++++++++ .../jackson/databind/ObjectMapperTest.java | 10 +++++ .../databind/deser/JacksonTypesDeserTest.java | 44 ++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index 972b01adabe..03bcb2fb817 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -980,6 +980,51 @@ public T readValue(JsonParser p, Class valueType) throws JacksonException return (T) _readValue(_deserializationContext(p), 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 JacksonException if there is a problem processing the JSON + */ + public T readObject(JsonParser p, T... reified) throws JacksonException { + 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))); + } + + /** + * 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 JacksonException if there is a problem processing the JSON + */ + public T readObject(String content, T... reified) throws JacksonException { + 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 into a Java type, reference * to which is passed as argument. Type is passed using so-called diff --git a/src/test/java/tools/jackson/databind/ObjectMapperTest.java b/src/test/java/tools/jackson/databind/ObjectMapperTest.java index 5423a31ec08..675a82158fd 100644 --- a/src/test/java/tools/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/tools/jackson/databind/ObjectMapperTest.java @@ -146,6 +146,16 @@ public void testConfigForPropertySorting() throws Exception assertFalse(dc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)); } + @Test + public void testAutoDetectClasses() throws Exception + { + ObjectMapper m = newJsonMapper(); + final String JSON = "{ \"x\" : 3 }"; + + Bean bean = m.readObject(JSON); + assertNotNull(bean); + } + @Test public void testDeserializationContextCache() throws Exception { diff --git a/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java b/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java index fb62f5713b6..2609378f573 100644 --- a/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java +++ b/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java @@ -83,6 +83,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.readObject(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.readObject(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.readObject(p); + String str = mapper.readObject(buf.asParser()); + assertEquals("abc", str); + + // and ditto for last one + buf = mapper.readValue(p, TokenBuffer.class); + Map map = mapper.readObject(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 b4766dd92d7844fed56b5f5c131b45d2c4e79be9 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Fri, 12 Sep 2025 09:44:44 +0530 Subject: [PATCH 2/6] Removed readObject method. Fixed test case to check for type --- .../tools/jackson/databind/ObjectMapper.java | 39 ++++------------ .../jackson/databind/ObjectMapperTest.java | 10 ----- .../jackson/databind/ObjectReaderTest.java | 12 +++++ .../databind/deser/JacksonTypesDeserTest.java | 44 ------------------- 4 files changed, 20 insertions(+), 85 deletions(-) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index 03bcb2fb817..d3248af0045 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -981,20 +981,14 @@ public T readValue(JsonParser p, Class valueType) throws JacksonException } /** - * 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 JacksonException if there is a problem processing the JSON - */ - public T readObject(JsonParser p, T... reified) throws JacksonException { - 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))); + * Factory method for constructing {@link ObjectReader} that will + * read or update instances of specified type + */ + public ObjectReader forAutomaticType(T... reified) { + Type type = getClassOf(reified); + _assertNotNull("type", type); + return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); } /** @@ -1008,23 +1002,6 @@ 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 JacksonException if there is a problem processing the JSON - */ - public T readObject(String content, T... reified) throws JacksonException { - 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 into a Java type, reference * to which is passed as argument. Type is passed using so-called diff --git a/src/test/java/tools/jackson/databind/ObjectMapperTest.java b/src/test/java/tools/jackson/databind/ObjectMapperTest.java index 675a82158fd..5423a31ec08 100644 --- a/src/test/java/tools/jackson/databind/ObjectMapperTest.java +++ b/src/test/java/tools/jackson/databind/ObjectMapperTest.java @@ -146,16 +146,6 @@ public void testConfigForPropertySorting() throws Exception assertFalse(dc.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)); } - @Test - public void testAutoDetectClasses() throws Exception - { - ObjectMapper m = newJsonMapper(); - final String JSON = "{ \"x\" : 3 }"; - - Bean bean = m.readObject(JSON); - assertNotNull(bean); - } - @Test public void testDeserializationContextCache() throws Exception { diff --git a/src/test/java/tools/jackson/databind/ObjectReaderTest.java b/src/test/java/tools/jackson/databind/ObjectReaderTest.java index 759f2f18904..5dfda415435 100644 --- a/src/test/java/tools/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/tools/jackson/databind/ObjectReaderTest.java @@ -53,6 +53,18 @@ public void testSimpleViaParser() throws Exception assertTrue(ob instanceof List); } + @Test + public void testSimpleViaParser1() throws Exception + { + final String JSON = "[1]"; + JsonParser p = MAPPER.createParser(JSON); + List ob = MAPPER.forAutomaticType() + .readValue(p); + p.close(); + assertTrue(ob != null); + assertEquals(1, ob.get(0)); + } + @Test public void testSimpleAltSources() throws Exception { diff --git a/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java b/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java index 2609378f573..fb62f5713b6 100644 --- a/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java +++ b/src/test/java/tools/jackson/databind/deser/JacksonTypesDeserTest.java @@ -83,50 +83,6 @@ 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.readObject(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.readObject(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.readObject(p); - String str = mapper.readObject(buf.asParser()); - assertEquals("abc", str); - - // and ditto for last one - buf = mapper.readValue(p, TokenBuffer.class); - Map map = mapper.readObject(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 3d22384f34e641a0a0ecc6fa26e413d5bdf382cf Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Fri, 12 Sep 2025 09:45:20 +0530 Subject: [PATCH 3/6] Removed readObject method. Fixed test case to check for type --- src/test/java/tools/jackson/databind/ObjectReaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/tools/jackson/databind/ObjectReaderTest.java b/src/test/java/tools/jackson/databind/ObjectReaderTest.java index 5dfda415435..537b8869669 100644 --- a/src/test/java/tools/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/tools/jackson/databind/ObjectReaderTest.java @@ -54,7 +54,7 @@ public void testSimpleViaParser() throws Exception } @Test - public void testSimpleViaParser1() throws Exception + public void testSimpleViaParserForAutomaticType() throws Exception { final String JSON = "[1]"; JsonParser p = MAPPER.createParser(JSON); From 44cc0914e411bab18c2ada6510b0ae9b81db38d0 Mon Sep 17 00:00:00 2001 From: Rohan Philip Lopes Date: Fri, 12 Sep 2025 09:48:08 +0530 Subject: [PATCH 4/6] JavaDoc fix. --- src/main/java/tools/jackson/databind/ObjectMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index 8dbb4553f2c..ddaada4d997 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -985,7 +985,7 @@ public T readValue(JsonParser p, Class valueType) throws JacksonException /** * Factory method for constructing {@link ObjectReader} that will - * read or update instances of specified type + * read or update instances automatically */ public ObjectReader forAutomaticType(T... reified) { Type type = getClassOf(reified); From 49043c7069e9a3b6c50b7b1cc98bff202f3980ad Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 12 Sep 2025 14:53:44 -0700 Subject: [PATCH 5/6] Reorder, rename, add Javadocs --- .../tools/jackson/databind/ObjectMapper.java | 61 ++++++++++++------- .../jackson/databind/ObjectReaderTest.java | 26 ++++---- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index ddaada4d997..0bc47d46bde 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; @@ -983,28 +984,6 @@ public T readValue(JsonParser p, Class valueType) throws JacksonException return (T) _readValue(_deserializationContext(p), p, _typeFactory.constructType(valueType)); } - /** - * Factory method for constructing {@link ObjectReader} that will - * read or update instances automatically - */ - public ObjectReader forAutomaticType(T... reified) { - Type type = getClassOf(reified); - _assertNotNull("type", type); - return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, - null, _injectableValues); - } - - /** - * 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 to deserialize JSON content into a Java type, reference * to which is passed as argument. Type is passed using so-called @@ -2252,6 +2231,44 @@ public ObjectReader readerForMapOf(Class type) { null, _injectableValues); } + /** + * Factory method for constructing {@link ObjectReader} that will + * read or update instances of auto-detected type; inferred from compiled + * info. Note that the nominal parameter MUST be an empty array; + * otherwise an exception will be thrown. + * Usage: + *
+     *  MyType result = objectMapper.readerForDetectedType().readValue(json);
+     *
+ * + * @since 3.0 + */ + public ObjectReader readerForDetectedType(T... reified) { + // Should be auto-generated empty array; verify + Objects.requireNonNull(reified, "reified"); + if (reified.length != 0) { + throw new IllegalArgumentException(String.format( +"argument \reified\" should be empty; has %d elements", reified.length)); + } + Type type = _getClassOf(reified); + _assertNotNull("type", type); + return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, + null, _injectableValues); + } + + /** + * 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 + * + * @since 3.0 + */ + private static Class _getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + /** * Factory method for constructing {@link ObjectReader} that will * use specified {@link JsonNodeFactory} for constructing JSON trees. diff --git a/src/test/java/tools/jackson/databind/ObjectReaderTest.java b/src/test/java/tools/jackson/databind/ObjectReaderTest.java index 537b8869669..555fa5603d0 100644 --- a/src/test/java/tools/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/tools/jackson/databind/ObjectReaderTest.java @@ -53,18 +53,6 @@ public void testSimpleViaParser() throws Exception assertTrue(ob instanceof List); } - @Test - public void testSimpleViaParserForAutomaticType() throws Exception - { - final String JSON = "[1]"; - JsonParser p = MAPPER.createParser(JSON); - List ob = MAPPER.forAutomaticType() - .readValue(p); - p.close(); - assertTrue(ob != null); - assertEquals(1, ob.get(0)); - } - @Test public void testSimpleAltSources() throws Exception { @@ -102,7 +90,7 @@ public void testReaderForArrayOf() throws Exception assertEquals(ABC.C, abcs[1]); } - // [databind#2693]: convenience read methods: + // [databind#2693]: convenience read methods @Test public void testReaderForListOf() throws Exception { @@ -112,7 +100,7 @@ public void testReaderForListOf() throws Exception assertEquals(Arrays.asList(ABC.B, ABC.C), value); } - // [databind#2693]: convenience read methods: + // [databind#2693]: convenience read methods @Test public void testReaderForMapOf() throws Exception { @@ -122,6 +110,16 @@ public void testReaderForMapOf() throws Exception assertEquals(Collections.singletonMap("key", ABC.B), value); } + // [databind#5064]: auto-detection of type + @Test + public void testReaderForDetectedType() throws Exception + { + List ob = MAPPER.readerForDetectedType() + .readValue("[1]"); + assertNotNull(ob); + assertEquals(1, ob.get(0)); + } + /* /********************************************************** /* Test methods, some alternative JSON settings From 526d9ffae89d73b1bd26b256c9c1f0a84d903041 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 12 Sep 2025 15:00:05 -0700 Subject: [PATCH 6/6] Add meaningful tests (that, alas, fail) --- .../java/tools/jackson/databind/ObjectMapper.java | 1 + .../tools/jackson/databind/ObjectReaderTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index 0bc47d46bde..c1cfb4e47f7 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -2251,6 +2251,7 @@ public ObjectReader readerForDetectedType(T... reified) { "argument \reified\" should be empty; has %d elements", reified.length)); } Type type = _getClassOf(reified); +System.err.println("DEBUG: reified type: "+type); _assertNotNull("type", type); return _newReader(deserializationConfig(), _typeFactory.constructType(type), null, null, _injectableValues); diff --git a/src/test/java/tools/jackson/databind/ObjectReaderTest.java b/src/test/java/tools/jackson/databind/ObjectReaderTest.java index 555fa5603d0..a5ad12ebb1a 100644 --- a/src/test/java/tools/jackson/databind/ObjectReaderTest.java +++ b/src/test/java/tools/jackson/databind/ObjectReaderTest.java @@ -114,10 +114,23 @@ public void testReaderForMapOf() throws Exception @Test public void testReaderForDetectedType() throws Exception { + // First, regular "untyped" (java.lang.Object) List ob = MAPPER.readerForDetectedType() .readValue("[1]"); assertNotNull(ob); assertEquals(1, ob.get(0)); + + // then simple pojo + Point point = MAPPER.readerForDetectedType().readValue("{\"x\":1,\"y\":2}"); + assertNotNull(point); + assertEquals(1, point.x); + assertEquals(2, point.y); + + // and more complex + POJO pojo = MAPPER.readerForDetectedType().readValue("{\"name\":{\"value\":123}}"); + assertNotNull(pojo); + assertNotNull(pojo.name); + assertEquals(Map.of("value", 123), pojo.name); } /*