Jackson. Как прервать десериализацию объекта и передать в поле вложенный объект в качестве строки JSON?
Необходимо с помщью Jackson десериализовать объект, но так, чтоб в некотором поле десериализация прекратилась, и вложенный json записался в поле в виде строки.
@Data
public class A {
private String a;
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
final String json = "{\"a\": {\"b\": 123}}";
final A a = mapper.readValue(json, A.class);
System.out.println(a);
}
}
В примере ожидаю, что в поле a будет содержать строку со значением "{\"b\": 123}". Но если просто указать тип поля String, то возникает следующая ошибка:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (String)"{"a": {"b": 123}}"; line: 1, column: 7] (through reference chain: A["a"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1420)
at com.fasterxml.jackson.databind.DeserializationContext.extractScalarFromObject(DeserializationContext.java:932)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
at A.main(A.java:24)
Эту проблему решить можно, если вручную написать десериализатор:
@Data
@JsonDeserialize(using = A.ADeserializer.class)
public class A {
private String a;
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
final String json = "{\"a\": {\"b\": 123}}";
final A a = mapper.readValue(json, A.class);
System.out.println(a); // A(a={"b":123})
}
static class ADeserializer extends StdDeserializer<A> {
public ADeserializer() {
this(null);
}
protected ADeserializer(Class<?> vc) {
super(vc);
}
@Override
public A deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
final JsonNode node = p.getCodec().readTree(p);
final String subJson = node.get("a").toString();
final A a = new A();
a.setA(subJson);
return a;
}
}
}
Но это решение очень громоздкое даже для одного поля.
Есть ли в Jackson какое-то более компактное решение этой проблемы из коробки?
Ответы (1 шт):
Автор решения: Roman-Stop RU aggression in UA
→ Ссылка
Создайте десериализатор для типа String:
class StringDeserializer extends StdDeserializer<String> {
public StringDeserializer() {
this(null);
}
protected ADeserializer(Class<?> vc) {
super(vc);
}
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
final JsonNode node = p.getCodec().readTree(p);
return node.toString();
}
}
И используйте его на любых полях String любых классов:
@Data
public class A {
@JsonDeserialize(using = StringDeserializer.class)
private String a;
...
}