【问题标题】:Jackson JSON mapping to object with tree traversal杰克逊 JSON 映射到具有树遍历的对象
【发布时间】:2014-06-19 13:45:12
【问题描述】:

我有以下 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;
  }
}

是否有某种 Jackson 注释可以查看,例如:

@SomeAnnotation("response.original.status.code")
private String status;

我正在使用这样的 restTemplate 调用 JSON 服务(它为我提供上面的 JSON 响应):

ResponseEntity<Structure> response = restTemplate.postForEntity(endpoint, requestObject, Structure.class);

【问题讨论】:

    标签: java json mapping jackson resttemplate


    【解决方案1】:

    没有将对象字段映射到树中的 Json 节点的 Jackson 注释,但实现起来并不难。以下是需要做的 3 件事:

    1. 使用路径值创建自定义注释。
    2. 创建一个自定义反序列化器,它将根据注释的路径值定位节点并将其转换为字符串。
    3. 注册一个annotation introspector,它会告诉 Jackson 使用您的注解注解的字段或参数已映射到 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 Documentation 表示如果你的 pom.xml 中有这个依赖,并且你的 spring 上下文中有标签 &lt;mvc:annotation-driven&gt;,Spring MVC 会自动为你的 REST 模板注册一个 JSON 转换器

      <dependency>
         <groupId>org.codehaus.jackson</groupId>
         <artifactId>jackson-mapper-asl</artifactId>
         <version>1.9.3</version>
      </dependency>
      

      您的 JSON 字符串和 Java 对象存在一些不一致之处。 例如。 Status 也应该是带域代码的类型,而不是字符串

      【讨论】:

      • 我有这个依赖,这不是问题。但我不想使用包含字段状态的字段原始字段创建一个类响应,该字段包含字段代码。这是冗长的方式。我希望将 JSON 结构展平一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-10
      • 1970-01-01
      • 1970-01-01
      • 2013-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多