Here is your problem - Pojo<?> deserialized = mapper.readValue(json, Pojo.class)
.
ObjectMapper does not know what type to parse T
to. To tell it the type you need to use either readValue
overload with TypeReference, or the overload with JavaType. I find JavaType
easier to read, so here is an example with it:
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
String json = "{\"value\": \"2022-02-22T12:00:00\"}";
JavaType javaType = TypeFactory.defaultInstance().constructParametricType(Pojo.class, LocalDateTime.class);
Pojo<LocalDateTime> deserialized = mapper.readValue(json, javaType);
System.out.println(deserialized.values[0].toLocalDate());
System.out.println(deserialized.values[0].toLocalTime());
}
}
When constructing the parametric JavaType
the first argument is the class itself, next are concrete generic types.
Edit: Having in mind the new info, the best i can come up with is custom deserializer, which resolves the type at runtime. Something like this:
public class UnknownTypeDeserializer<T> extends StdDeserializer<Pojo<T>> {
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
public UnknownTypeDeserializer() {
super((Class<?>) null);
}
@Override
public Pojo<T> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = parser.getCodec().readTree(parser);
String value = node.get("value").asText();
Pojo<T> pojo = new Pojo<>();
T[] arr;
try {
arr = (T[]) new LocalDateTime[]{LocalDateTime.parse(value, this.formatter)};
} catch (Exception exc) {
try {
arr = (T[]) new Integer[]{Integer.parseInt(value)};
} catch (NumberFormatException numberFormatException) {
arr = (T[]) new String[]{value};
}
}
pojo.values = arr;
return pojo;
}
}
The idea is try to parse to LocalDateTime
, if not possible, try to parse to int
, if not possible again leave as String
. That's quite the ugly way to do it, but i am trying just to illustrate the idea. Instead of catching exceptions, it might be better to examine the format, using regex for example, and according to which regex matches, parse to correct type. I tested it with string and int, and it actually works.
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Pojo.class, new UnknownTypeDeserializer<>());
mapper.registerModule(simpleModule);
String json1 = "{\"value\": \"2022-02-22T12:00:00\"}";
Pojo<?> deserialized1 = mapper.readValue(json1, Pojo.class);
System.out.println("is date - " + (deserialized1.values[0] instanceof LocalDateTime));
String json2 = "{\"value\": \"bla bla bla\"}";
Pojo<?> deserialized2 = mapper.readValue(json2, Pojo.class);
System.out.println("is string - " + (deserialized2.values[0] instanceof String));
String json3 = "{\"value\": 41}";
Pojo<?> deserialized3 = mapper.readValue(json3, Pojo.class);
System.out.println("is int - " + (deserialized3.values[0] instanceof Integer));
}
}