#java #json #mapping #jackson #resttemplate
#java #json #сопоставление #джексон #resttemplate
Вопрос:
У меня есть следующий JSON, и я хотел бы сопоставить его с объектом.
{
"response": {
"original": {
"status": {
"code": "SUCCESS"
},
"organisationNode": [
{
"organisationId": {
"identifier": "2005286047",
"identifierType": "BECBE"
},
"organisationLevel": "Subdivision",
"organisationCode": "ENT_CBE",
"organisationParentNode": {
"organisationId": {
"identifier": "0878016185",
"identifierType": "BEEUN"
},
"organisationLevel": "Entity",
"organisationCode": "ENT_CBE"
}
}
]
}
}
}
И я хочу, чтобы мой объект Java выглядел примерно так:
public class Structure {
private String status; //SUCCESS
private List<OrganisationNode> organisationNodes;
public class OrganisationNode {
private String organisationId; //2005286047
private String organisationLevel; //Subdivision
private List<OrganisationNode> organisationNodes;
}
}
Есть ли какая-то аннотация Джексона, которую можно увидеть, например:
@SomeAnnotation("response.original.status.code")
private String status;
Я вызываю службу JSON (которая предоставляет мне ответ JSON выше) с RestTemplate следующим образом:
ResponseEntity<Structure> response = restTemplate.postForEntity(endpoint, requestObject, Structure.class);
Ответ №1:
Нет аннотации Джексона, которая сопоставляет поле объекта с узлом Json в дереве, но это не сложно реализовать. Вот 3 вещи, которые необходимо выполнить:
- Создайте пользовательскую аннотацию со значением пути.
- Создайте пользовательский десериализатор, который определит местоположение узла в соответствии со значением пути аннотации и преобразует его в строку.
- Зарегистрируйте интроспектор аннотаций, который сообщит Джексону, что поле или параметр, аннотированный вашей аннотацией, сопоставлены со свойством json и должны использовать ваш десериализатор.
Вот полный пример:
public class JacksonContextualSerializer {
@Retention(RetentionPolicy.RUNTIME)
public static @interface SomeAnnotation {
String value();
}
public static final String JSON = "{n"
" "response": {n"
" "original": {n"
" "status": {n"
" "code": "SUCCESS"n"
" }n"
" }n"
" }n"
"}";
public static class PathAwareSerializer extends JsonDeserializer<String>
implements ContextualDeserializer {
private String path;
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property)
throws JsonMappingException {
// when the serializer implements the ContextualDeserializer, then we have
// the access to the element's annotation value
path = property.getMember().getAnnotation(SomeAnnotation.class).value();
return this;
}
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
// read JSON as tree
JsonNode node = jp.readValueAs(JsonNode.class);
// find the last node starting from the second path element, since the first
// is covered by the property name (see the introspector)
String[] pathArray = path.split("\.");
for (int i = 1; i < pathArray.length; i ) {
node = node.get(pathArray[i]);
}
return node.asText();
}
}
public static class Bean {
private final String status;
@JsonCreator
public Bean(@SomeAnnotation("response.original.status.code") String status) {
this.status = status;
}
@Override
public String toString() {
return "Bean{"
"status='" status '''
'}';
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
// register an introspector which will inject jackson annotations to elements that are
// marked by the custom annotation.
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@Override
public PropertyName findNameForDeserialization(Annotated a) {
if (a.hasAnnotation(SomeAnnotation.class)) {
// if annotation is present then this is a property which name is the first
// element in the path
String path = a.getAnnotation(SomeAnnotation.class).value();
return new PropertyName(path.split("\.")[0]);
}
return super.findNameForDeserialization(a);
}
@Override
public Class<? extends JsonDeserializer<?>> findDeserializer(Annotated a) {
// if the annotation is present, then the property is deserialized using
// the custom serializer
if (a.hasAnnotation(SomeAnnotation.class)) {
return PathAwareSerializer.class;
}
return super.findDeserializer(a);
}
});
System.out.println(mapper.readValue(JSON, Bean.class));
}
}
Вывод:
Bean{status='SUCCESS'}
Ответ №2:
В документации Spring говорится, что если у вас есть эта зависимость в вашем pom.xml и тег <mvc:annotation-driven>
в вашем контексте spring, Spring MVC автоматически регистрирует конвертер JSON для вашего шаблона REST
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.3</version>
</dependency>
В вашей строке JSON и объекте Java есть несколько несоответствий.
Например. Status
также должен быть тип с кодом поля, а не со строкой
Комментарии:
1. У меня есть эта зависимость, это не проблема. Но я не хочу создавать ответ класса с исходным полем, содержащим статус поля, содержащий код поля. Это способ подробного описания. Я хочу, чтобы структура JSON была немного сглажена.