JAXB и множественные объектные отношения - PullRequest
4 голосов
/ 08 октября 2009

Мы используем Jersey (библиотека 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 детей.
Каждый родитель имеет свою собственную копию child2.

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

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

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

Ответы [ 5 ]

8 голосов
/ 07 июля 2010

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>
3 голосов
/ 08 октября 2009

Это не проблема 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 имеет поддержку ссылок на объекты , когда он сериализует / десериализует графы объектов, но это полностью собственный механизм.

1 голос
/ 08 октября 2009

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

0 голосов
/ 08 октября 2009

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

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

0 голосов
/ 08 октября 2009

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

...