JAXB сортировка исключительно из интерфейсов - PullRequest
11 голосов
/ 06 марта 2012

У меня есть сложная иерархия Java-интерфейсов, которую я хотел бы маршалировать (и не обязательно демаршалировать) с JAXB.Эти интерфейсы представляют объекты, которые будут возвращены из REST API JAX-RS в виде XML, JSON, YAML и т. Д. (Я использую RestEasy, который может маршировать JAXB-аннотированные типы в форматах, отличных от XML.)

Проблема в том, что JAXB в основном ориентирован на класс.Я провел множество веб-исследований о трудностях с JAXB и интерфейсами, наиболее близкими из которых являются MOXy JAXB - сопоставление интерфейсов с XML и JAXB и интерфейсными моделями .Тем не менее, у меня есть две основные проблемы: а) я хочу аннотировать / работать с точки зрения интерфейсов, а не конкретных классов (которых будет несколько реализаций и содержащих значительное другое состояние, которое не должно быть маршализовано), и б) я имеюнесколько уровней наследования интерфейса.Вот пример интерфейсов, за исключением каких-либо аннотаций JAXB:

interface Uuided {
  UUID getId();
}
interface Named {
  String getName();
}
interface Component extends Uuided, Named {
  Map<String, ComponentAttribute> getAttributes();
}
interface Attribute extends Named {
  Type getType();
  Object getValue();
}
interface ComponentAttribute extends Attribute {
  Component getDeclaringComponent();
}

В идеале это будет генерировать что-то вроде:

<component id="xxx" name="thing">
  <attributes>
    <componentAttribute name="color">
      <type><stringType/></type>
      <value>green</value>
      <declaringComponent idref="xxx"/>
    </componentAttribute>
  </attributes>
</component>

Ясно, что в абстрактном плане это приводит к проблемамкак определение наиболее производного аннотированного интерфейса, теоретически их может быть несколько.В моем случае, однако, я считаю, что конкретные классы реализуют только один интерфейс, который следует маршалировать.Unmarshaling не является необходимым, так как у меня есть отдельные классы, определяющие свойства upsert.

Итак, мой вопрос, возможно ли вообще работать с JAXB, и если да, то как?Даже если мне нужно быть очень явным при определении привязок, адаптеров и т. Д., Я бы хотел работать в рамках JAXB, чтобы получить выгоду от всех не XML-провайдеров в RestEasy.

Ответы [ 2 ]

5 голосов
/ 19 января 2017

Краткий ответ: используйте @XmlElement(type = Object.class) в поле вашего интерфейса.

Подробности ниже:

Я нашел 2 способа, которыми вы можете заставить JAXB сериализовать ваши интерфейсы:

  1. @XmlAnyElement
  2. @XmlElement(type = Object.class)

1. @ XmlAnyElement

Просто аннотируйте поле типа интерфейса с помощью @XmlAnyElement, и JAXB будет сериализовать интерфейс из его конкретного типа. Не забудьте пометить конкретные типы с помощью @XmlRootElement и добавить конкретные типы в JAXBContext. Ниже приведен полный пример:

public class InterfaceSerializer {

    @XmlRootElement
    public static class Pojo {
        Pojo() {
            field1 = new PojoFieldImpl1();
            field2 = new PojoFieldImpl2();
            field3 = new PojoFieldImpl1();
        }

        @XmlAnyElement
        public IPojoField field1;
        @XmlAnyElement
        public IPojoField field2;
        @XmlAnyElement
        public IPojoField field3;

        @Override
        public String toString() {
            return "field1 = " + field1 + "\nfield2 = " + field2 + "\nfield3 = " + field3;
        }
    }

    public static interface IPojoField {

    }

    @XmlRootElement
    public static class PojoFieldImpl1 implements IPojoField {

        PojoFieldImpl1() {
            value = "PojoFieldImpl1 value";
        }

        public String value;

        @Override
        public String toString() {
            return value;
        }
    }

    @XmlRootElement
    public static class PojoFieldImpl2  implements IPojoField {

        PojoFieldImpl2() {
            value = "PojoFieldImpl2 value1";
            value2 = "PojoFieldImpl2 value2";
        }

        public String value;
        public String value2;

        @Override
        public String toString() {
            return value + " " + value2;
        }
    }

    public static void main(String []args) throws JAXBException {
        Pojo pojo = new Pojo();
        JAXBContext jaxbContext = JAXBContext.newInstance(Pojo.class, PojoFieldImpl1.class, PojoFieldImpl2.class);
        Marshaller marshaller = jaxbContext.createMarshaller();

        marshaller.marshal(pojo, new File("interfaceSerializer.xml"));
    }
}

Выходной XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pojo>
    <pojoFieldImpl1>
        <value>PojoFieldImpl1 value</value>
    </pojoFieldImpl1>
    <pojoFieldImpl2>
        <value>PojoFieldImpl2 value1</value>
        <value2>PojoFieldImpl2 value2</value2>
    </pojoFieldImpl2>
    <pojoFieldImpl1>
        <value>PojoFieldImpl1 value</value>
    </pojoFieldImpl1>
</pojo>

Недостатки этого метода:

  • вы не можете отличить от XML каждое отдельное поле от вашего pojo (одинаковые реализации будут записаны с одним и тем же тегом)
  • у вас нет информации о типе Unmarshall вашего XML (если вы хотите это сделать)

Эти недостатки исправлены во втором решении :

2. @ XmlElement (type = Object.class)

Я наткнулся на это в сообщении в блоге mikesir87 . Просто замените @XmlAnyElement аннотации сверху на @XmlElement(type = Object.class) У вас должно быть что-то вроде этого в классе Pojo сверху:

@XmlElement(type = Object.class)
public IPojoField field1;
@XmlElement(type = Object.class)
public IPojoField field2;
@XmlElement(type = Object.class)
public IPojoField field3;

Повторно запустив наш пример, получившийся XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pojo>
    <field1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1">
        <value>PojoFieldImpl1 value</value>
    </field1>
    <field2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl2">
        <value>PojoFieldImpl2 value1</value>
        <value2>PojoFieldImpl2 value2</value2>
    </field2>
    <field3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1">
        <value>PojoFieldImpl1 value</value>
    </field3>
</pojo>

Это также можно десериализовать:

Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Pojo unmarshalledPojo = (Pojo) unmarshaller.unmarshal(new File("interfaceSerializer.xml"));  
System.out.println(unmarshalledPojo);

Результирующий вывод:

field1 = PojoFieldImpl1 value
field2 = PojoFieldImpl2 value1 PojoFieldImpl2 value2
field3 = PojoFieldImpl1 value

Возможно, решение " hackish ", но оно выполняет свою работу.

1 голос
/ 10 апреля 2012

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

В итоге я написал собственную структуру маршалинга с собственным набором аннотаций:

@MarshalMixin // marshal fields but not a top-level object
interface Uuided {
  @MarshalAsString // ignore properties; just call toString()
  @MarshalId // treat as identifier for @MarshalUsingIds or cyclic ref
  UUID getId();
}
@MarshalMixin
interface Named {
  @MarshalId
  String getName();
}
@MarshalObject // top-level marshaled object providing class name
interface Component extends Uuided, Named {
  @MarshalAsKeyedObjectMap(key = "name") // see description below
  Map<String, ComponentAttribute> getAttributes();
}
@MarshalObject
interface Attribute extends Named {
  Type getType();
  @MarshalDynamic // use run-time (not declared) type
  Object getValue();
}
interface ComponentAttribute extends Attribute {
  @MarshalUsingIds
  Component getDeclaringComponent();
}

Сгенерированные маршалеры записывают на уровень абстракции (в настоящее время реализованы для JSON и XML). Это дает гораздо больше гибкости, чтобы сделать вывод естественным для разных представлений без тонны аннотаций и адаптеров. Например, то, что я называю картами объектов с ключами, где каждый объект содержит свой ключ карты. В JSON требуется карта, а в XML - последовательность:

{..., map: {'a': {'name': 'a', ...}, 'b': {'name: 'b', ...}, ...}, ...}
...<map><thing name='a'>...</thing><thing name='b'>...</thing></map>...

Похоже, что 4 других человека тоже заботятся об этом, так что, надеюсь, я смогу открыть их в конце концов. : -)

...