JAXB2: Наследование и атрибут type с Lombok - PullRequest
0 голосов
/ 25 июня 2019

Я хочу создать XML-представление моей внутренней структуры объектов. Из того, что я нашел, JAXB2 выглядит многообещающим кандидатом. Я только что закончил делать пару уроков ( Link1 , Link2 ) и у меня есть вопрос, который очень важен для меня.

У меня есть два класса POJO1 и POJO2 одного и того же супертипа AbstractPOJO. Все сгенерированы с использованием проекта lombok Я хочу получить список всех AbstractPOJO в моем документе XML. Они могут быть типа POJO1 или POJO2.

Я бы, наконец, ожидал что-то вроде

<model>
  <pojos>
    <pojo1 name="pojo1name" type="POJO1">
      <Foo>1</Foo>
      <Bar>2.0</Bar>
    </pojo1>
    <pojo2 name="pojo2name" type="POJO2">
      <Foo>1</Foo>
      <Baz>"name"</Baz>
    </pojo2>
  <pojos>
</model>

Мои вопросы:

  • Как изменить имя списка с AbstractPOJO на POJOs?
  • Как добавить атрибут типа с именем класса к каждому элементу в списке AbstractPOJO?

MWE

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.w3c.dom.Document;

public class Test {

    public static void main(String[] args) throws FileNotFoundException, JAXBException, ParserConfigurationException, TransformerException {

        Foo foo = new Foo(1);

        POJO1 p1 = new POJO1();
        p1.setName("pojo1name");
        p1.setFoo(foo);
        p1.setBar(new Bar(2.0));

        POJO2 p2 = new POJO2();
        p2.setName("pojo2name");
        p2.setFoo(foo);
        p2.setBaz(new Baz("name"));

        Memory mem = new Memory();
        mem.add(p1);
        mem.add(p2);

        // JAXB object
        Object jaxbElement = mem;

        File file = new File("./memory.xml");
        FileOutputStream fop = new FileOutputStream(file);

        // create JAXB context
        JAXBContext context = JAXBContext.newInstance(jaxbElement.getClass());

        // Creating the Document object
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();

        // Marshaling the User object into a Document object
        Marshaller m = context.createMarshaller();
        m.marshal(jaxbElement, document);

        // writing the Document object to console
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();

        // set the properties for a formatted output - if false the output will be on one single line
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(fop);
        transformer.transform(source, result);
    }

    @Data
    public static class AbstractPOJO {
        private String name;
        private Foo foo;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    public static class POJO1 extends AbstractPOJO {
        private Bar bar;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    public static class POJO2 extends AbstractPOJO {
        private Baz baz;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Foo {
        private int id;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Bar {
        private double val;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Baz {
        private String name;
    }

    @Data
    public static class Memory {

        private Collection<AbstractPOJO> list;

        public void add(AbstractPOJO v){
            if (this.list == null){this.list = new ArrayList<>();}
            list.add(v);
        }
    }
}

Я не включил ни одной аннотации JAXB в пример, так как я все еще нахожусь на крутой кривой обучения.


Обновление

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.w3c.dom.Document;

public class Test {

    public static void main(String[] args) throws FileNotFoundException, JAXBException, ParserConfigurationException, TransformerException {

        Foo foo = new Foo(1);

        POJO1 p1 = new POJO1();
        p1.setName("pojo1name");
        p1.setFoo(foo);
        p1.setBar(new Bar(2.0));

        POJO2 p2 = new POJO2();
        p2.setName("pojo2name");
        p2.setFoo(foo);
        p2.setBaz(new Baz("name"));

        Memory mem = new Memory();
        mem.add(p1);
        mem.add(p2);

        // JAXB object
        Object jaxbElement = mem;

        File file = new File("./memory.xml");
        FileOutputStream fop = new FileOutputStream(file);

        // create JAXB context
        JAXBContext context = JAXBContext.newInstance(jaxbElement.getClass());

        // Creating the Document object
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();

        // Marshaling the User object into a Document object
        Marshaller m = context.createMarshaller();
        m.marshal(jaxbElement, document);

        // writing the Document object to console
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();

        // set the properties for a formatted output - if false the output will be on one single line
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(fop);
        transformer.transform(source, result);
    }

    @Data
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class AbstractPOJO {

        @XmlAttribute
        private String name;
        private Foo foo;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    //@XmlRootElement(name = "pojo1")
    public static class POJO1 extends AbstractPOJO {
        private Bar bar;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    //@XmlRootElement(name = "pojo2")
    public static class POJO2 extends AbstractPOJO {
        private Baz baz;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Foo {
        private int id;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Bar {
        private double val;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Baz {
        private String name;
    }

    @Data
    @XmlRootElement(name="Memory")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Memory {

        @XmlElementWrapper(name ="POJOs")
        @XmlElement(type=AbstractPOJO.class, name="POJO")
        private Collection<AbstractPOJO> list;

        public void add(AbstractPOJO v){
            if (this.list == null){this.list = new ArrayList<>();}
            list.add(v);
        }
    }
}

Поэтому я добавил несколько аннотаций JAXB, чтобы посмотреть, что я получу. Результат

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Memory>
  <POJOs>
    <POJO name="pojo1name">
      <foo>
        <id>1</id>
      </foo>
    </POJO>
    <POJO name="pojo2name">
      <foo>
        <id>1</id>
      </foo>
    </POJO>
  </POJOs>
</Memory>

Итак, некоторый прогресс, но все же есть некоторые проблемы:

  • Как получить атрибут типа, например, <POJO name="pojo1name" type="POJO1">
  • В настоящее время отображается только информация из абстрактного суперкласса AbstractPOJO. Как я могу получить фактические атрибуты реализации там?

Я попытался сделать AbstractPOJO XmlTransient и добавить XmlRootElement и XmlAccessorType к его реализациям POJO1 и POJO2. Однако тогда я получаю SAXException2: javax.xml.bind.JAXBException: Weder class Test$POJO1 noch eine der zugehörigen Superklassen ist diesem Kontext bekannt.. В переводе: Neither class POJO1 nor one of its superclasses is known to this context. Я могу добавить @XmlSeeAlso(POJO1.class) к Memory, но затем я получаю ту же ошибку для POJO2, и кажется невозможным добавить несколько классов в XmlSeeAlso. Так что я застрял.


Обновление 2 - Понял?

Добавление @XmlSeeAlso({POJO1.class,POJO2.class}) к Memory.

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.w3c.dom.Document;

public class Test {

    public static void main(String[] args) throws FileNotFoundException, JAXBException, ParserConfigurationException, TransformerException {

        Foo foo = new Foo(1);

        POJO1 p1 = new POJO1();
        p1.setName("pojo1name");
        p1.setFoo(foo);
        p1.setBar(new Bar(2.0));

        POJO2 p2 = new POJO2();
        p2.setName("pojo2name");
        p2.setFoo(foo);
        p2.setBaz(new Baz("name"));

        Memory mem = new Memory();
        mem.add(p1);
        mem.add(p2);

        // JAXB object
        Object jaxbElement = mem;

        File file = new File("./memory.xml");
        FileOutputStream fop = new FileOutputStream(file);

        // create JAXB context
        JAXBContext context = JAXBContext.newInstance(jaxbElement.getClass());

        // Creating the Document object
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();

        // Marshaling the User object into a Document object
        Marshaller m = context.createMarshaller();
        m.marshal(jaxbElement, document);

        // writing the Document object to console
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();

        // set the properties for a formatted output - if false the output will be on one single line
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(fop);
        transformer.transform(source, result);
    }

    @Data
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    //@XmlTransient
    public static class AbstractPOJO {

        @XmlAttribute
        private String name;
        private Foo foo;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class POJO1 extends AbstractPOJO {
        private Bar bar;
    }

    @Data
    @EqualsAndHashCode(callSuper=true)
    @XmlTransient
    public static class POJO2 extends AbstractPOJO {
        private Baz baz;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Foo {
        private int id;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Bar {
        private double val;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Baz {
        private String name;
    }

    @Data
    @XmlRootElement(name="Memory")
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlSeeAlso({POJO1.class,POJO2.class})
    public static class Memory {

        @XmlElementWrapper(name ="POJOs")
        @XmlElement(type=AbstractPOJO.class, name="POJO")
        private Collection<AbstractPOJO> list;

        public void add(AbstractPOJO v){
            if (this.list == null){this.list = new ArrayList<>();}
            list.add(v);
        }
    }
}

1080 * производит *

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Memory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <POJOs>
    <POJO name="pojo1name" xsi:type="pojo1">
      <foo>
        <id>1</id>
      </foo>
      <bar>
        <val>2.0</val>
      </bar>
    </POJO>
    <POJO name="pojo2name" xsi:type="pojo2">
      <foo>
        <id>1</id>
      </foo>
      <baz>
        <name>name</name>
      </baz>
    </POJO>
  </POJOs>
</Memory>

с package-info.java из здесь .

Тем не менее, это правильный способ сделать это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...