通过从JDK 1.7升级到JDK 1.8 u05集合来破解JAXB配置

时间:2014-05-14 20:29:32

标签: xml-parsing jaxb migration java-8 persistence

下面的代码曾经在JDK 1.7使用的JAXB实现下工作,但现在在JDK 1.8下它已经破解了。在下面的代码中,您将找到似乎使其在1.8中工作的关键更改。 1.8下的“修复”并不是真正的修复,因为暴露内部集合以供外部世界直接修改是不好的做法。我想通过我的类控制对内部列表的访问,我不想通过创建可观察的集合并听取它们来使事情复杂化。这是不可接受的。

有没有办法让我的原始代码在JD 1.8的JAXB下工作?

 @XmlElementWrapper(name = "Wrap")
   @XmlElement(name = "Item", required = true)
   public synchronized void setList(List<CustomObject> values) {
     list.clear();
     list.addAll(values);
   }

public synchronized List<CustomObject> getList() {
//      return new ArrayList(list); // this was the original code that worked under 1.7
      return list; //this is the only thing that works under 1.8
   }

经过更多分析后,问题似乎来自于JAXB,而不再为集合调用setter方法(它曾经在JDK 1.7下使用过)。现在在JDK 1.8下,它调用getter并直接修改集合。这带来了几个问题:

1 - 强制用户将内部集合暴露给外界进行免费修改(不良做法) 2 - 不允许用户在列表更改时执行任何自定义代码(例如,如果调用setter,您可以执行此操作)。有可能创建一个可观察的集合并听取它,但这是一个比调用setter方法更复杂的解决方法。

1 个答案:

答案 0 :(得分:2)

背景

当在JAXB中映射集合属性时,它首先检查getter以查看collection属性是否已预先初始化。在下面的示例中,我希望将我的属性公开为List<String>,但支持实现为LinkedList,可以容纳1000个项目。

private List<String> foos = new LinkedList<String>(1000);

@XmlElement(name="foo")
public List<String> getFoos() {
    return foos;
}

为什么您的代码曾经工作

如果您以前让JAXB在映射到从getter返回非null响应的集合的属性上调用setter,则该JAXB实现中存在错误。您的代码也不应该在以前的版本中有效。

如何获取被设置者

让setter调用你只需要让你的getter在对象的新实例上返回null。您的代码可能类似于:

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

@XmlRootElement(name = "Foo")
public class Foo {

    private List<CustomObject> list = null;

    @XmlElementWrapper(name = "Wrap")
    @XmlElement(name = "Item", required = true)
    public synchronized void setList(List<CustomObject> values) {
        if (null == list) {
            list = new ArrayList<CustomObject>();
        } else {
            list.clear();
        }
        list.addAll(values);
    }

    public synchronized List<CustomObject> getList() {
        if (null == list) {
            return null;
        }
        return new ArrayList(list);
    }

}

更新

如果您不需要在JAXB解组返回的List上执行任何逻辑,那么使用字段访问可能是一种可接受的解决方案。

@XmlRootElement(name = "Foo")
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {

    @XmlElementWrapper(name = "Wrap")
    @XmlElement(name = "Item", required = true)
    private List<CustomObject> list = null;

    public synchronized void setList(List<CustomObject> values) {
        if(null == list) {
            list = new ArrayList<CustomObject>();
        } else {
            list.clear();
        }
        list.addAll(values);
    }

    public synchronized List<CustomObject> getList() {
        return new ArrayList(list);
    }

}