如何使用JAXB XmlAdapter来编组列表?

时间:2013-01-28 19:08:24

标签: java json jaxb jersey xmladapter

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

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。我已XmlAdapter注册LocalDateLocalTime

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

更新

通过添加标量LocalTime属性并观察JSON中的预期输出,我确认LocalTime编组确实适用于标量LocalTime属性。

为了完整性,这里是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");

1 个答案:

答案 0 :(得分:4)

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

<强> StringAdapter

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

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。这将为此包中的所有映射XmlAdapter属性注册此String(请参阅: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注释以考虑映射的属性。

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转换为大写。

<?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只包含一个空标签,   这意味着JAXB不喜欢POJO模型。   (也许它应该是Serializable?)

JAXB不要求POJO实现Serializable

  

这很有趣,因为它似乎表明唯一的一部分   JAXB在这方面的作用是借给它的注释和其他一些   接口(例如XmlAdapter)到JSON(de)序列化器,那就是   关系结束的地方。

这取决于用作JSON绑定层的内容。 JAXB (JSR-222)规范不包含JSON绑定,因此这种类型的支持超出了规范。 EclipseLink JAXB(MOXy)提供本机JSON绑定(我是MOXy主管),你可以这样做:

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考虑在内:

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

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

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