JAXB, как навязать определенный порядок в демаршаллинге - PullRequest
0 голосов
/ 21 сентября 2018

У меня есть следующий 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 имеет переопределенные методы equals и getHashCode.Указанный объект (заданный referenceId) влияет на хеш-код.Это означает, что A Link без установленного referenceId имеет хеш-код, отличный от установленного referenceId.

  2. Collection содержитHashSet<Link>, который заполняется Link с во время демаршаллинга.Это означает: A Link, который был помещен в Set, не должен впоследствии изменять свой хэш-код;иначе он не будет найден методом 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 добавляется только к HashSet<Link> ПОСЛЕ его referenceIdбыл установлен?

  • Изменение XML не является вариантом;Я получаю его от третьей стороны и не могу его изменять *.(Переключение элементов collection и referencable решит проблему, потому что тогда Link получает свой referenceId, установленный до того, как он будет добавлен в Set<Link>.)

  • Изменение HashSet на какую-либо коллекцию, в которой не используется getHashCode, не вариант;Мне нужен contains метод с O(1) производительностью *.

  • Изменение getHashCode метода Link не вариант;Мне это нужно.В противном случае он становится бесполезным.

* Сценарий реальной жизни связан с огромными XML-файлами, в которых коллекция содержит тысячи ссылок.


RootElement:

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

1 Ответ

0 голосов
/ 21 сентября 2018

Вы не можете управлять разрешением идентификаторов.

Обходные пути:

  • Вы можете удалить хеш-код ссылочного объекта из хеш-кода 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;
    }
    
...