【问题标题】:JSON data binding with custom logic using Jackson使用 Jackson 与自定义逻辑进行 JSON 数据绑定
【发布时间】:2014-06-13 10:02:19
【问题描述】:

我已经定义了我想要反序列化为 Java 对象的 JSON 响应。我设法使用树模型“手动”完成它,但如果可能的话,我想使用数据绑定。问题是我需要一些自定义逻辑来处理某些部分。

JSON 看起来像这样:

{
    "resourcedescriptions": [
        {
            "path": "somePath",
            "tag_pagetype": "default",
            "tag_bookingcenter": [
                "bc_ch",
                "bc_de"
            ],
            "resources": [
                {
                    "path": "somePathDe.html",
                    "lang": "de",
                    "lastmodified": 1399020442914,
                    "mimetype": "text/html"
                },
                {
                    "path": "somePathEn.html",
                    "lang": "en",
                    "lastmodified": 1399907224208,
                    "mimetype": "text/html"
                }
            ],
            "lastmodified": 1399907224208
        },
        {
            "path": "someOtherPath",
            "tag_pagetype": "special",
            "tag_bookingcenter": [
                "bc_ch"
            ],
            "resources": [
                {
                    "path": "someOtherPathDe.html",
                    "lang": "de",
                    "lastmodified": 1399020442914,
                    "mimetype": "text/html"
                },
                {
                    "path": "someOtherPathEn.html",
                    "lang": "en",
                    "lastmodified": 1399907224208,
                    "mimetype": "text/html"
                }
            ],
            "lastmodified": 1399907224208
        }
    ]
}

我的 Java 类是:

public class ResourceDescription {
    private String path;
    private LocalDateTime lastModified;
    private String chartConfig;
    private final List<Tag> tags = new ArrayList<Tag>();
    private final List<Resource> resources = new ArrayList<Resource>();
}
public class Resource {
    private String lang;
    private String path;
    private String mimeType;
    private LocalDateTime lastModified;
}
public class Tag {
    private String namespace;
    private String name;
}

第一个问题,即使在这里阅读了很多帖子,我仍然无法完全理解。如何将 JSON 中的资源数组反序列化到我的 ResourceDescription 列表中?

第二个也是最复杂的问题。以“tag_”为前缀的 JSON 属性需要转换为 Tag 类,而属性名称表示命名空间,值(单个或数组)表示名称。因此,如果模式是“namespace:name”,则第一个 ResourceDescription 将具有以下标签:

  • tag_pagetype:默认
  • tag_bookingcenter:bc_ch
  • tag_bookingcenter:bc_de

第三个“lastmodified”应该从 Joda-Time 转换为 DateTime。

这甚至可以通过数据绑定实现,还是我应该坚持树模型?

【问题讨论】:

    标签: java json data-binding jackson


    【解决方案1】:

    您需要为 ResourceDescription 创建一个自定义反序列化程序,以完成您需要做的事情。为 ResourceDescription 指定自定义反序列化器的语法如下所示:

    @JsonDeserialize(using=ResourceDescriptionDeserializer.class)
    public class ResourceDescription { ... }
    

    此反序列化程序必须遍历每个资源描述的每个键,以查看它是否以“tag_”开头,去掉前缀并将剩余的用于命名空间,并在添加之前填充标签的名称/值将它添加到正在创建的 ResourceDescription 的数组中。

    对于所有其他属性/类型,我认为您可以遵循默认反序列化并在各自的字段上设置这些属性。

    然后,要反序列化 ResourceDescriptions 列表,您可以指定 TypeReference 以避免为 ResourceDescriptions 编写自定义反序列化程序。代码将如下所示:

        Map<String, List<ResourceDescription>> resultMap =
                objectMapper.readValue(JSON, new TypeReference<Map<String, List<ResourceDescription>>>() {});
        List<ResourceDescription> descriptions = resultMap.get("resourcedescriptions");
    

    这是一篇与您正在做的事情不太相符的文章,但我认为这将有助于了解总体思路:

    Using Jackson to deserialize array nested within array in JSON object

    【讨论】:

      【解决方案2】:

      如何将 JSON 中的资源数组反序列化到我的 ResourceDescription 列表?

      您必须创建包含 resourcedescriptions 属性的附加 root 类。例如:

      class Root {
      
          private List<ResourceDescription> resourcedescriptions;
      
          public List<ResourceDescription> getResourcedescriptions() {
              return resourcedescriptions;
          }
      
          public void setResourcedescriptions(List<ResourceDescription> resourcedescriptions) {
              this.resourcedescriptions = resourcedescriptions;
          }
      
          @Override
          public String toString() {
              return String.valueOf(resourcedescriptions);
          }
      }
      

      以“tag_”为前缀的JSON属性需要转化为 Tag 类,而属性名称表示命名空间 值(单个或数组)表示名称。

      您可以使用@JsonAnySetter 注释来处理这种情况。您必须向 ResourceDescription 类添加新方法,如下所示:

      @JsonAnySetter
      public void setAnyValues(String propertyName, Object value) {
          if (propertyName.startsWith("tag_")) {
              if (value instanceof String) {
                  tags.add(new Tag(propertyName, value.toString()));
              } else if (value instanceof List) {
                  List<?> values = (List<?>) value;
                  for (Object v : values) {
                      tags.add(new Tag(propertyName, v.toString()));
                  }
              }
              // throw exception?
          } else {
              // handle another unknown properties
          }
      }
      

      第三个“lastmodified”应该从 乔达时间。

      您可以通过添加jackson-datatype-joda 库来处理JodaTime 类型。添加后即可注册JodaModule模块。

      mapper.registerModule(new JodaModule());
      

      您的JSON 包含使用小写字母编写的属性,但您的POJO 属性使用驼峰式编写的其他问题。您可以更改JSONPOJO 或使用@JsonProperty("property-name-from-JSON") 注释或实现自己的命名策略。例如:

      mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.PropertyNamingStrategyBase() {
          @Override
          public String translate(String propertyName) {
              return propertyName.toLowerCase();
          }
      });
      

      完整的 Java 示例如何反序列化您的 JSON

      import java.util.ArrayList;
      import java.util.List;
      
      import org.joda.time.LocalDateTime;
      
      import com.fasterxml.jackson.annotation.JsonAnySetter;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.fasterxml.jackson.databind.PropertyNamingStrategy;
      import com.fasterxml.jackson.datatype.joda.JodaModule;
      
      public class JacksonProgram {
      
          public static void main(String[] args) throws Exception {
              String json = "{ ... }";
              ObjectMapper mapper = new ObjectMapper();
              mapper.registerModule(new JodaModule());
              mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.PropertyNamingStrategyBase() {
                  @Override
                  public String translate(String propertyName) {
                      return propertyName.toLowerCase();
                  }
              });
      
              System.out.println(mapper.readValue(json, Root.class));
          }
      }
      
      class Root {
      
          private List<ResourceDescription> resourcedescriptions;
      
          public List<ResourceDescription> getResourcedescriptions() {
              return resourcedescriptions;
          }
      
          public void setResourcedescriptions(List<ResourceDescription> resourcedescriptions) {
              this.resourcedescriptions = resourcedescriptions;
          }
      
          @Override
          public String toString() {
              return String.valueOf(resourcedescriptions);
          }
      }
      
      class ResourceDescription {
      
          private String path;
          private LocalDateTime lastModified;
          private String chartConfig;
          private final List<Tag> tags = new ArrayList<Tag>();
          private final List<Resource> resources = new ArrayList<Resource>();
      
          @JsonAnySetter
          public void setAnyValues(String propertyName, Object value) {
              if (propertyName.startsWith("tag_")) {
                  if (value instanceof String) {
                      tags.add(new Tag(propertyName, value.toString()));
                  } else if (value instanceof List) {
                      List<?> values = (List<?>) value;
                      for (Object v : values) {
                          tags.add(new Tag(propertyName, v.toString()));
                      }
                  }
                  // throw exception?
              } else {
                  // handle another unknown properties
              }
          }
      
          public String getPath() {
              return path;
          }
      
          public void setPath(String path) {
              this.path = path;
          }
      
          public LocalDateTime getLastModified() {
              return lastModified;
          }
      
          public void setLastModified(LocalDateTime lastModified) {
              this.lastModified = lastModified;
          }
      
          public String getChartConfig() {
              return chartConfig;
          }
      
          public void setChartConfig(String chartConfig) {
              this.chartConfig = chartConfig;
          }
      
          public List<Tag> getTags() {
              return tags;
          }
      
          public List<Resource> getResources() {
              return resources;
          }
      
          @Override
          public String toString() {
              return "ResourceDescription [path=" + path + ", lastModified=" + lastModified
                      + ", chartConfig=" + chartConfig + ", tags=" + tags + ", resources=" + resources
                      + "]";
          }
      }
      
      class Resource {
      
          private String lang;
          private String path;
          private String mimeType;
          private LocalDateTime lastModified;
      
          public String getLang() {
              return lang;
          }
      
          public void setLang(String lang) {
              this.lang = lang;
          }
      
          public String getPath() {
              return path;
          }
      
          public void setPath(String path) {
              this.path = path;
          }
      
          public String getMimeType() {
              return mimeType;
          }
      
          public void setMimeType(String mimeType) {
              this.mimeType = mimeType;
          }
      
          public LocalDateTime getLastModified() {
              return lastModified;
          }
      
          public void setLastModified(LocalDateTime lastModified) {
              this.lastModified = lastModified;
          }
      
          @Override
          public String toString() {
              return "Resource [lang=" + lang + ", path=" + path + ", mimeType=" + mimeType
                      + ", lastModified=" + lastModified + "]";
          }
      }
      
      class Tag {
      
          private String namespace;
          private String name;
      
          public Tag() {
          }
      
          public Tag(String namespace, String name) {
              this.namespace = namespace;
              this.name = name;
          }
      
          public String getNamespace() {
              return namespace;
          }
      
          public void setNamespace(String namespace) {
              this.namespace = namespace;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          @Override
          public String toString() {
              return "Tag [namespace=" + namespace + ", name=" + name + "]";
          }
      }
      

      上面的程序打印:

      [ResourceDescription [path=somePath, lastModified=2014-05-12T17:07:04.208, chartConfig=null, tags=[Tag [namespace=tag_pagetype, name=default], Tag [namespace=tag_bookingcenter, name=bc_ch], Tag [namespace=tag_bookingcenter, name=bc_de]], resources=[Resource [lang=de, path=somePathDe.html, mimeType=text/html, lastModified=2014-05-02T10:47:22.914], Resource [lang=en, path=somePathEn.html, mimeType=text/html, lastModified=2014-05-12T17:07:04.208]]], ResourceDescription [path=someOtherPath, lastModified=2014-05-12T17:07:04.208, chartConfig=null, tags=[Tag [namespace=tag_pagetype, name=special], Tag [namespace=tag_bookingcenter, name=bc_ch]], resources=[Resource [lang=de, path=someOtherPathDe.html, mimeType=text/html, lastModified=2014-05-02T10:47:22.914], Resource [lang=en, path=someOtherPathEn.html, mimeType=text/html, lastModified=2014-05-12T17:07:04.208]]]]
      

      【讨论】:

      • 非常感谢您提供如此广泛的回复和示例。我试试看。
      猜你喜欢
      • 2019-01-10
      • 2011-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多