Я хочу создать 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
из здесь .
Тем не менее, это правильный способ сделать это?