使用Jackson的JSON数据与自定义逻辑绑定

时间:2014-06-13 10:02:20

标签: java json data-binding jackson

我已经定义了JSON响应,我希望将其反序列化为Java Objects。我设法做到这一点"手动"使用树模型,但如果可能,我想使用数据绑定。问题是我需要为某些部分提供一些自定义逻辑。

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列表?

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

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

&#34;&#34; lastmodified&#34;应该从Joda-Time转换为DateTime。

这是否可以通过数据绑定实现,还是应该坚持使用树模型?

2 个答案:

答案 0 :(得分:1)

您需要为ResourceDescription创建自定义反序列化器,以便完成您需要执行的操作。为ResourceDescription指定自定义反序列化器的语法如下所示:

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

这个反序列化器必须遍历每个资源描述的每个键,以查看它是否以&#34; tag _&#34;开头,剥去前缀并使用剩余的名称空间并填充名称/值在将标记添加到正在创建的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

答案 1 :(得分:1)

  

如何将这个资源数组从JSON反序列化为我的   ResourceDescription列表?

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

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);
    }
}
  

以&#34;标记_&#34;为前缀的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
    }
}
  

&#34;&#34; lastmodified&#34;应该转换为DateTime   约达时间。

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

mapper.registerModule(new JodaModule());

其他问题是JSON包含使用小写编写的属性,但您的POJO属性是使用camel-case编写的。您可以更改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]]]]