2

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

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/JAXBtimes使用已注册的来编组列表中的每个元素XmlAdapter

更新:

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

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

1 回答 1

4

一个XmlAdapter类应用于该类型的映射字段/属性,在集合的情况下,应用于集合中的每个项目。下面的例子证明这是可行的。您是否尝试过将示例独立运行到 XML 以验证映射。怀疑问题出在XmlAdapter具体问题之外。

字符串适配器

以下XmlAdapter将在解组操作中将 a 转换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具有两个映射属性。第一个是 type String,第二个是List<String>. 关于您的课程,我注意到的一件事Day是您只有get方法。这意味着您需要@XmlElement为 JAXB 实现添加注释以考虑映射属性。

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 模型。(也许它应该是可序列化的?)

JAXB 不要求 POJO 实现Serializable.

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

这取决于用作 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 绑定层中的问题。

于 2013-01-29T10:29:05.507 回答