【问题标题】:JAXB Configuration was broken by upgrading from JDK 1.7 to JDK 1.8 u05 for collectionsJAXB 配置因集合从 JDK 1.7 升级到 JDK 1.8 u05 而被破坏
【发布时间】:2014-07-03 02:10:39
【问题描述】:

下面的代码曾经在 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 方法要复杂得多。

【问题讨论】:

  • 我也从 NetBeans 7.4 更新到了 8.0,这似乎也有影响。

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


【解决方案1】:

背景

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

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

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

为什么你的代码过去可以工作

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

如何调用 Setter

要调用 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 解组返回的列表执行任何逻辑,那么使用字段访问可能是一个可接受的解决方案。

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

}

【讨论】:

  • 感谢您的解释。我真的很感谢你的帮助。我的代码在以前的版本中确实有效,我认为 JAXB 不应该做出这些假设。就我而言,我确实在实例变量中初始化了一个新的空列表,这是为了避免到处检查空值,因为我不允许将列表设置为空值。似乎这样我将不得不创建第二个假的 getter 和 setter 并将它们设置为私有(如果这甚至可以工作)仅为 JAXB 并返回 null 如果列表为空。为什么 JAXB 会这样做。如果我对其进行了注释,它应该无条件地调用 setter
  • 如果你从getter返回一个空列表,你确定xml会被正确加载吗?因为return new ArrayList(list);没有加载xml(它被保存只是没有加载)。我很确定即使调用了 setter,返回 null 也会导致不加载该属性。
  • @Coder - 你应该会看到 setter 调用了一个填充列表。那你在你的二传手里做什么呢?
  • 清除当前列表并添加所有值,如我上面的代码所示,但是传递给我的列表是空的,即使它保存在 xml 中。我不确定您是否可以尝试一个简单的示例,因为它似乎对我不起作用。
  • 似乎要让它与 setter 一起正常工作并在不暴露内部列表变量的情况下仍然加载的唯一方法是执行此链接中提到的操作:stackoverflow.com/questions/14986562/… 请参阅已回答的帖子2013 年 2 月 20 日 20:05
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-09
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多