JAXB и множественные отношения объектов

Мы используем Джерси (библиотека Java REST) ​​для проекта в течение последних нескольких месяцев, и нам это нравится. Но на этой неделе столкнулись с проблемой JAXB.

У меня есть элемент, у которого есть 2 дочерних элемента, у каждого из которых есть дочерние элементы, где некоторые из их дочерних элементов ссылаются друг на друга.

Позвольте мне показать код.

Root root = new Root();

Parent parent1 = new Parent();
Parent parent2 = new Parent();

root.add(parent1);
root.add(parent2);

Child child1 = new Child();
Child child2 = new Child();
Child child3 = new Child();

parent1.add(child1);
parent1.add(child2);

parent2.add(child2);
parent2.add(child3);

Итак, у нас есть 1 корень, 2 родителя и 3 ребенка.

Если я отправлю это вверх и вниз по пути JAXB, я, похоже, верну 4 дочерних элемента.
У каждого родителя есть собственная копия дочернего элемента2.

Есть ли способ заставить JAXB сериализовать отношения и показать, что и parent1, и parent2 указывают на один и тот же объект?

Мы обнаружили эту проблему только недавно, когда передавались более сложные элементы.

Если нет способа заставить JAXB сделать это (это то, во что я верю на данный момент), есть ли у кого-нибудь какие-либо предложения о том, как я мог бы сотворить магию в Джерси, чтобы восстановить отношения?


person stevemac    schedule 07.10.2009    source источник


Ответы (5)


Это проблема не JAXB, а вашей модели. Как бы вы хотели, чтобы JAXB отображал ваши отношения в XML, когда XML не предоставляет стандартного механизма для выражения этого? И потребитель, и производитель XML должны иметь слой бизнес-логики, который бы согласовывался с представлением.

Я предлагаю вам изменить ваши данные. Например, вместо того, чтобы содержать детей внутри родителей, вы можете сгладить все:

<parent id="parent1">
   <child ref="child1"/>
   <child ref="child2"/>
</parent>

<parent id="parent2">
   <child ref="child2"/>
   <child ref="child3"/>
</parent>

<child id="child1"/>
<child id="child2"/>
<child id="child3"/>

Какой бы механизм ни читал и не писал эту структуру, он должен знать, что все это значит, но я не вижу другого хорошего способа сделать это.

Еще один интересный момент заключается в том, что XStream имеет поддержка ссылок на объекты при сериализации/десериализации графов объектов, но это полностью проприетарный механизм.

person skaffman    schedule 08.10.2009

JAXB поддерживает ссылки без ограничения между объектами в дереве с использованием комбинации @XmlID/@XmlIDREF. Требованием для этого является то, что на все объекты в дереве также должны ссылаться отношения включения. В вашей модели это может означать предоставление Root коллекции Child.

Ниже будет модифицированная версия вашего кода:

Root root = new Root(); 

Parent parent1 = new Parent(); 
Parent parent2 = new Parent(); 

root.add(parent1); 
root.add(parent2); 

Child child1 = new Child(); 
child1.id = "1";
root.add(child1);
parent1.add(child1); 

Child child2 = new Child();
child2.id = "2";
root.add(child2);
parent1.add(child2); 
parent2.add(child2); 

Child child3 = new Child();
child3.id = "3";
root.add(child3);
parent2.add(child3);

Тогда ваши классы моделей будут выглядеть так:

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Root {

    public List<Parent> parent = new ArrayList<Parent>();
    public List<Child> child = new ArrayList<Child>();

    public void add(Parent parent1) {
        parent.add(parent1);
    }

    public void add(Child child1) {
        child.add(child1);
    }
}

import javax.xml.bind.annotation.XmlIDREF;

public class Parent {

    @XmlIDREF
    public List<Child> child = new ArrayList<Child>();

    public void add(Child child1) {
        child.add(child1);
    }

}

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;

public class Child {

    @XmlID
    @XmlAttribute
    public String id;

}

Результирующий XML будет выглядеть так:

<root>
    <parent>
        <child>1</child>
        <child>2</child>
    </parent>
    <parent>
        <child>2</child>
        <child>3</child>
    </parent>
    <child id="1"/>
    <child id="2"/>
    <child id="3"/>
</root>
person bdoughan    schedule 06.07.2010

потенциально вы могли бы сделать это, написав класс адаптера для класса Root, который при разупорядочении может очистить дочерние объекты, чтобы удалить дубликаты.

person jjoshi    schedule 07.10.2009

Небольшая мысль: реализуют ли объекты Child правильный метод equals()?

person Fabian Steeg    schedule 07.10.2009

Как уже говорилось в других ответах. Это дизайн JAXB.

Что вы можете сделать, так это вручную склеить несколько копий того, что, по вашему мнению, было одним и тем же объектом перед сериализацией. Тогда вам не понадобится собственный уникальный идентификатор для объектов, чтобы вы могли отличать клоны от других объектов.

person Grzegorz Oledzki    schedule 08.10.2009