Сопоставление Jackson JSON с объектом с обходом по дереву

#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 вещи, которые необходимо выполнить:

  1. Создайте пользовательскую аннотацию со значением пути.
  2. Создайте пользовательский десериализатор, который определит местоположение узла в соответствии со значением пути аннотации и преобразует его в строку.
  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 была немного сглажена.