我有以下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解组(下面的代码(。请注意以下几点:
Link
已覆盖equals
和getHashCode
方法。引用的对象(由referenceId
给出(影响哈希代码。这意味着:未设置referenceId
的Link
的哈希代码与设置其referenceId
时不同。Collection
包含一个HashSet<Link>
,该在解组期间填充Link
。这意味着:已放入Set
的Link
之后不得更改其哈希代码;否则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 是没有选择的;我从第三方获得它,不允许修改它*。(切换
collection
和referencable
元素将解决此问题,因为这样,Link
会在将其添加到Set<Link>
之前设置其referenceId
。将
HashSet
更改为不使用getHashCode
的集合是没有选择的;我需要一种性能O(1)
contains
方法*。更改
Link
getHashCode
方法是没有选择的;我需要这样。否则它就变得毫无用处。
*现实生活中的场景是关于巨大的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; }