Hello Jackson community,
The input Json have nested fields and different field names. I am looking for a way to deserilize using JsonPointer espression (because it allows path lookup).
I have tried several approaches but none of them worked so far.
- Tried using AnnotationIntrospector = introspector gets invoked which returns a custom deserializer from overridden findDeserializer(). However, custom JsonDeserialiser.deserialize() never invoked on mapper.readValue()
- Tried using ContextualDeserilizer = it gets invoked which returns a custom deserializer. However, custom JsonDeserialiser.deserialize() never invoked on mapper.readValue()
The customer deserialiser only invoked when it is declared on the class (not on field).
Following are classes:
Using jackson with lombok
// custom annotation
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonPointerProperty {
String value();
}
// Person POJO
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
private String name;
private Address address;
}
// Address POJO
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Address {
// using custom annotation
@JsonPointerProperty("/requestModel/streetName")
private String street;
private String city;
private String postcode;
}
// introspector
public class JsonPointerPropertyIntrospector
extends NopAnnotationIntrospector {
@Override
public Object findDeserializer(Annotated am) {
final JsonPointerProperty jsonPointerProperty = am.getAnnotation(JsonPointerProperty.class);
if (jsonPointerProperty != null) {
return new JsonPointerPropertyDeserializer(
jsonPointerProperty.value(),
am.getType().getRawClass()
);
}
return super.findDeserializer(am);
}
}
// custom deserializer
public class JsonPointerPropertyDeserializer
extends JsonDeserializer<Object> {
private final String jsonPath;
private final Class<?> clazz;
public JsonPointerPropertyDeserializer() {
this(null, null);
}
public JsonPointerPropertyDeserializer(String jsonPath,
Class<?> clazz) {
this.jsonPath = jsonPath;
this.clazz = clazz;
}
@Override
public Object deserialize(JsonParser parser, DeserializationContext ctx)
throws IOException, JacksonException {
// not reaching here
JsonNode jsonNode = parser.readValueAs(JsonNode.class);
JsonNode at = jsonNode.at(jsonPath);
String value = ctx.readTreeAsValue(jsonNode, String.class);
System.out.println(value);
return null;
}
}
// Unit test
class PersonTest {
String json;
@BeforeEach
void setup() throws IOException {
json = "{ 'address': { 'requestModel': { 'streetName': 'some street', 'cityName': 'some city' }}}"
.replace(
"'",
"\""
);
}
@Test
void testPerson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JsonPointerPropertyIntrospector());
final Person person = mapper.readValue(json, Person.class);
assertEquals("some street", person.getAddress().getStreet(), "Street mismatch");
}
}
There is some error parsing `requestModel`, but I was hoping it would at least reach to customer deserializer.
Any suggestions or point me what I am doing wrong?