Skip to content

Autodetect classes in ObjectMapper #5071

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

Open
wants to merge 6 commits into
base: 3.x
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
54 changes: 54 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3055,6 +3055,23 @@ public <T> T readValue(JsonParser p, Class<T> valueType)
return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType));
}

/**
* Reads the value from the given JSON content and automatically detects the class type.
*
* @param <T> 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> 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
Expand Down Expand Up @@ -3855,6 +3872,34 @@ public <T> T readValue(String content, Class<T> valueType)
return readValue(content, _typeFactory.constructType(valueType));
}

/**
* Utility method to get the class type from a varargs array.
*
* @param <T> the generic type
* @param array the varargs array
* @return the class of the array component
*/
private static <T> Class<T> getClassOf(T[] array) {
return (Class<T>) array.getClass().getComponentType();
}

/**
* Reads the value from the given JSON content and automatically detects the class type.
*
* @param <T> 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> 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.
*
Expand Down Expand Up @@ -3957,6 +4002,15 @@ public <T> T readValue(byte[] src, int offset, int len,
return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType));
}

@SuppressWarnings("unchecked")
public <T> 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> T readValue(byte[] src, TypeReference<T> valueTypeRef)
throws IOException, StreamReadException, DatabindException
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down