JAXB XmlAdapter如何用于编组列表? [英] How can JAXB XmlAdapter be used to marshall lists?

查看:76
本文介绍了JAXB XmlAdapter如何用于编组列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此类的实例是大型对象图的一部分,而不是对象图的根:

Instances of this class are part of a large object graph and are not at the root of the object graph:

public class Day
{
    public Day(LocalDate date, List<LocalTime> times)
    {
        this.date = date;
        this.times = times;
    }

    public Day()
    {
        this(null, null);
    }

    public LocalDate getDate()
    {
        return date;
    }

    public List<LocalTime> getTimes()
    {
        return times;
    }

    private final LocalDate date;

    private final List<LocalTime> times;
}

使用Jersey和JAXB将对象图转换为JSON.我已经为LocalDateLocalTime注册了XmlAdapter.

The object graph is converted to JSON using Jersey and JAXB. I have XmlAdapters registered for LocalDate and LocalTime.

问题在于它仅适用于date属性,而不适用于times属性.我怀疑这与times是一个列表而不是单个值有关.那么,如何告诉Jersey/JAXB使用注册的XmlAdapter整理times列表中的每个元素?

The problem is that it's only working for the date property and not the times property. I suspect this has something to do with the fact that times is a list rather than a single value. How, then, do I tell Jersey/JAXB to marshall each element in the times list using the registered XmlAdapter?

更新:

我通过添加标量LocalTime属性并观察JSON中的预期输出,确认LocalTime编组确实对标量LocalTime属性有效.

I confirmed that LocalTime marshalling is indeed working for scalar LocalTime properties by adding a scalar LocalTime property and observing the expected output in the JSON.

为完整起见,这里是package-info.java:

For completeness, here's package-info.java:

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(value = LocalDateAdapter.class, type = LocalDate.class),
    @XmlJavaTypeAdapter(value = LocalTimeAdapter.class, type = LocalTime.class)
})
package same.package.as.everything.else;

LocalDateAdapter:

public class LocalDateAdapter extends XmlAdapter<String, LocalDate>
{
    @Override
    public LocalDate unmarshal(String v) throws Exception
    {
        return formatter.parseLocalDate(v);
    }

    @Override
    public String marshal(LocalDate v) throws Exception
    {
        return formatter.print(v);
    }

    private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd");
}

LocalTimeAdapter:

public class LocalTimeAdapter extends XmlAdapter<String, LocalTime>
{
    @Override
    public LocalTime unmarshal(String v) throws Exception
    {
        return formatter.parseLocalTime(v);
    }

    @Override
    public String marshal(LocalTime v) throws Exception
    {
        return formatter.print(v);
    }

    private final DateTimeFormatter formatter = DateTimeFormat.forPattern("HHmm");

推荐答案

类的XmlAdapter应用于该类型的映射字段/属性,对于集合,则应用于集合中的每个项目.下面的示例证明了这一点.您是否尝试过将示例独立运行到XML以验证这种方式的映射.怀疑问题是XmlAdapter以外的其他问题.

An XmlAdapter for a class is applied to a mapped field/property of that type, and in the case of collections, for each item in the collection. The example below proves that this works. Have you tried running your example standalone to XML to verify the mappings that way. Is suspect the problem is something else other than the XmlAdapter specifically.

StringAdapter

以下XmlAdapter将在解组操作中将String转换为小写,并在编组后将其转换为大写.

The following XmlAdapter will convert a String to lower case on the unmarshal operation and convert it to upper case when marshalled.

package forum14569293;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class StringAdapter extends XmlAdapter<String, String> {

    @Override
    public String unmarshal(String v) throws Exception {
        return v.toLowerCase();
    }

    @Override
    public String marshal(String v) throws Exception {
        return v.toUpperCase();
    }

}

包装信息

就像在您的问题中一样,将使用包级别@XmlJavaTypeAdapters注释来注册XmlAdapter.这将为此包中所有映射的String属性注册此XmlAdapter(请参阅:

Just as in your question a package level @XmlJavaTypeAdapters annotation will be used to register the XmlAdapter. This will register this XmlAdapter for all mapped String properties within this package (see: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(value=StringAdapter.class, type=String.class)
})
package forum14569293;

import javax.xml.bind.annotation.adapters.*;

是一个类似于您的Day类的示例域模型,具有两个映射的属性.第一个是String类型,第二个是List<String>类型.关于您的Day类,我注意到的一件事是您只有get方法.这意味着您将需要为JAXB impl添加@XmlElement批注,以将其视为映射的属性.

Below is a sample domain model similar to your Day class with two mapped properties. The first is of type String and the second List<String>. One thing I notice about your Day class is that you only have get methods. This means that you will need to add an @XmlElement annotation for a JAXB impl to consider that a mapped property.

package forum14569293;

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Root {

    public Root(String foo, List<String> bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public Root() {
        this(null, null);
    }

    @XmlElement
    public String getFoo() {
        return foo;
    }

    @XmlElement
    public List<String> getBar() {
        return bar;
    }

    private final String foo;
    private final List<String> bar;

}

演示

package forum14569293;

import java.util.*;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        List<String> bar = new ArrayList<String>(3);
        bar.add("a");
        bar.add("b");
        bar.add("c");
        Root root = new Root("Hello World", bar);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);

    }

}

输出

下面是运行演示代码的输出,我们看到所有字符串都已由XmlAdapter转换为大写.

Below is the output from running the demo code we see that all the strings were converted to upper case by the XmlAdapter.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bar>A</bar>
    <bar>B</bar>
    <bar>C</bar>
    <foo>HELLO WORLD</foo>
</root>


更新

谢谢.我尝试了一下,XML只包含一个空标记, 意味着POX模型有一些JAXB不喜欢的东西. (也许应该可序列化?)

Thanks. I tried it and the XML consisted of one empty tag only, meaning there's something about the POJO model that JAXB doesn't like. (Perhaps it should be Serializable?)

JAXB不需要POJO实现Serializable.

JAXB does not require that POJOs implement Serializable.

这很有趣,因为它似乎表明唯一的部分 JAXB的作用是借用其注释和其他一些注释 接口(例如XmlAdapter)与JSON(反)序列化器的连接 关系结束的地方.

That's interesting because it seems to indicate that the only part JAXB plays in this is to lend its annotations and some other interfaces (e.g. XmlAdapter) to the JSON (de)serializer and that's where the relationship ends.

这取决于用作JSON绑定层的内容. JAXB(JSR-222) 规范没有涵盖了JSON绑定,因此这种类型的支持超出了规范. EclipseLink JAXB(MOXy)提供了本机JSON绑定(我是MOXy主管),您可以执行以下操作:

It depends on what is being used as the JSON binding layer. The JAXB (JSR-222) specification does not cover JSON-binding so this type of support is beyond the spec. EclipseLink JAXB (MOXy) offers native JSON-binding (I'm the MOXy lead) with it you could do something like:

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
marshaller.marshal(root, System.out);

并获得以下将XmlAdapter考虑在内的输出:

And get the following output that takes the XmlAdapter into account:

{
   "bar" : [ "A", "B", "C" ],
   "foo" : "HELLO WORLD"
}

尽管如此,当我有机会时,我将尽我所能 JAXB生成XML,并可能揭示其他内容.

Nevertheless, when I get an opportunity I will do what I can to get JAXB to generate the XML and that may reveal something else.

这很有用,因为我不相信您看到的是JAXB问题,而是您正在使用的JSON绑定层中的问题.

This would be useful as I do not believe what you are seeing is a JAXB issue, but an issue in the JSON binding layer that you are using.

这篇关于JAXB XmlAdapter如何用于编组列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆