JAXB如何在解组时强制某个顺序



我有以下XML:

<rootElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<collection>
<link referenceId="id1" foo="foo"/>
</collection>
<referencable id="id1" name="bar"/>
</rootElement>

我已经编写了一些类,这个XML应该通过JAXB解组(下面的代码(。请注意以下几点:

  1. Link已覆盖equalsgetHashCode方法。引用的对象(由referenceId给出(影响哈希代码。这意味着:未设置referenceIdLink的哈希代码与设置其referenceId时不同。

  2. Collection包含一个HashSet<Link>,该在解组期间填充Link。这意味着:已放入SetLink之后不得更改其哈希代码;否则contains方法将无法再找到它!

使用以下代码取消编组时

InputStream is = new FileInputStream("C:\rootElement.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(RootElement.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
RootElement rootElement = (RootElement)jaxbUnmarshaller.unmarshal(is);

Link最终找不到,可以通过调用轻松看到

Set<Link> links = rootElement.getCollection().getLink();
boolean contains = links.contains(links.iterator().next()); // returns false

所以我的问题是:如何确保Link仅在设置referenceId后添加到HashSet<Link>中?

  • 更改 XML 是没有选择的;我从第三方获得它,不允许修改它*。(切换collectionreferencable元素将解决此问题,因为这样,Link会在将其添加到Set<Link>之前设置其referenceId

  • HashSet更改为不使用getHashCode的集合是没有选择的;我需要一种性能O(1)contains方法*。

  • 更改LinkgetHashCode方法是没有选择的;我需要这样。否则它就变得毫无用处。

*现实生活中的场景是关于巨大的XML,其中集合包含数千个链接。


根元素:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"collection",
"referencable"
})
@XmlRootElement(name = "rootElement")
public class RootElement {
protected Collection collection;
protected Referencable referencable;
public Referencable getReferencable() { return referencable; }
public void setReferencable(Referencable referencable) { this.referencable = referencable; }
public Collection getCollection() { return collection; }
public void setCollection(Collection collection) { this.collection = collection; }
}

收集:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Collection", propOrder = {"link"})
public class Collection {
protected Set<Link> link;
public Set<Link> getLink() {
if (link == null) { link = new HashSet<Link>(); }
return this.link;
}
}

链接:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Link")
public class Link
{
@XmlAttribute(name = "referenceId", required = true)
@XmlIDREF
@XmlSchemaType(name = "IDREF")
protected Object referenceId;
public Object getReferenceId() { return referenceId; }
public void setReferenceId(Object value) { this.referenceId = value; }
@XmlAttribute(name = "foo", required = true)
protected String foo;
public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((foo == null) ? 0 : foo.hashCode());
result = prime * result + ((referenceId == null) ? 0 : referenceId.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
Link other = (Link) obj;
if (foo == null) { if (other.foo != null) { return false; } }
else if (!foo.equals(other.foo)) { return false; }
if (referenceId == null) { if (other.referenceId != null) { return false; } }
else if (!referenceId.equals(other.referenceId)) { return false; }
return true;
}
}

可参考:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Referencable")
public class Referencable
{
@XmlAttribute(name = "id", required = true)
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
@XmlID
@XmlSchemaType(name = "ID")
protected String id;
public String getId() { return id; }
public void setId(String value) { this.id = value; }
@XmlAttribute(name = "name")
protected String name;
public String getName() { return name; }
public void setName(String value) { this.name = value; }
@Override public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) { return true; }
if (!super.equals(obj)) { return false; }
if (getClass() != obj.getClass()) { return false; }
Referencable other = (Referencable) obj;
if (name == null) { if (other.name != null) { return false; } }
else if (!name.equals(other.name)) { return false; }
return true;
}
}

您无法控制何时解析 idref。

解决方法:

  • 您可以从Link的哈希代码中删除引用对象的哈希代码。它仍将是一个一致的哈希代码(但性能更差(
  • 将集合设为列表,除非您有数百个条目,否则您几乎看不到性能影响。
  • getLink()像这样

    protected List<Link> link;
    @XmlTransient
    private boolean reinit = false;
    public Set<Link> getLink() {
    if (!reinit) { 
    if(link != null) {
    link= new HashSet<Link>(link); 
    } else {
    link= new HashSet<Link>(); 
    }
    reinit = true
    } 
    return this.link;
    }
    

相关内容

  • 没有找到相关文章

最新更新