JAXB-маршалы XML по-разному относятся к OutputStream и StringWriter
Извиняюсь, если на это был дан ответ, но поисковые термины, которые я использовал (т.е. JAXB @XmlAttribute сконденсированный или JAXB XML-маршал для строк разных результатов) aren ' t придумывает что-нибудь.
Я использую JAXB для объектов un/marshal, аннотированных аннотациями @XmlElement
и @XmlAttribute
. У меня есть класс форматирования, который предоставляет два метода: один обертывает метод маршала и принимает объект для маршала и OutputStream
, другой просто принимает объект и возвращает результат XML как строку. К сожалению, эти методы не обеспечивают одинаковый вывод для одних и тех же объектов. При маршалинге файла простые поля объектов, помеченные @XmlAttribute
, печатаются как:
<element value="VALUE"></element>
в то время как при маршалинге на String они:
<element value="VALUE"/>
Я бы предпочел второй формат для обоих случаев, но мне любопытно, как контролировать разницу, и согласился бы на то, что они будут одинаковыми независимо. Я даже создал один статический маршаллер, который оба метода используют для устранения разных значений экземпляров. Код форматирования следует:
/** Marker interface for classes which are listed in jaxb.index */
public interface Marshalable {}
/** Local exception class */
public class XMLMarshalException extends BaseException {}
/** Class which un/marshals objects to XML */
public class XmlFormatter {
private static Marshaller marshaller = null;
private static Unmarshaller unmarshaller = null;
static {
try {
JAXBContext context = JAXBContext.newInstance("path.to.package");
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
unmarshaller = context.createUnmarshaller();
} catch (JAXBException e) {
throw new RuntimeException("There was a problem creating a JAXBContext object for formatting the object to XML.");
}
}
public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException {
try {
marshaller.marshal(obj, os);
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
public String marshalToString(Marshalable obj) throws XMLMarshalException {
try {
StringWriter sw = new StringWriter();
return marshaller.marshal(obj, sw);
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
}
/** Example data */
@XmlType
@XmlAccessorType(XmlAccessType.FIELD)
public class Data {
@XmlAttribute(name = value)
private String internalString;
}
/** Example POJO */
@XmlType
@XmlRootElement(namespace = "project/schema")
@XmlAccessorType(XmlAccessType.FIELD)
public class Container implements Marshalable {
@XmlElement(required = false, nillable = true)
private int number;
@XmlElement(required = false, nillable = true)
private String word;
@XmlElement(required = false, nillable = true)
private Data data;
}
Результат вызова marshal(container, new FileOutputStream("output.xml"))
и marshalToString(container)
выглядит следующим образом:
Выход в файл
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="project/schema">
<number>1</number>
<word>stackoverflow</word>
<data value="This is internal"></data>
</ns2:container>
и
Вывод на строку
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="project/schema">
<number>1</number>
<word>stackoverflow</word>
<data value="This is internal"/>
</ns2:container>
Ответы
Ответ 1
Похоже, это может быть "ошибка" в JAXB. Рассматривая источник, вызовы для маршала() создают разные авторы на основе параметра типа вывода/записи:
public void marshal(Object obj, OutputStream out, NamespaceContext inscopeNamespace) throws JAXBException {
write(obj, createWriter(out), new StAXPostInitAction(inscopeNamespace,serializer));
}
public void marshal(Object obj, XMLStreamWriter writer) throws JAXBException {
write(obj, XMLStreamWriterOutput.create(writer,context), new StAXPostInitAction(writer,serializer));
}
Реализации писателей различны в отношении того, как они обрабатывают "пустые элементы". Вышеприведенный код:
JAXB-ри\выполнения\SRC\COM\ВС\XML\привязывать\v2\выполнения\MarshallerImpl.java.
Два создаваемых вами автора:
JAXB-ри\выполнения\SRC\COM\ВС\XML\привязывать\v2\выполнения\вывода\UTF8XmlOutput.java
JAXB-ри\выполнения\SRC\COM\ВС\XML\привязывать\v2\выполнения\выход\XMLStreamWriterOutput.java
Ответ 2
Хорошей новостью является то, что JAXB - это спецификация с несколькими реализациями (как JPA). Если одна реализация не отвечает вашим потребностям, другие доступны, например EclipseLink JAXB (MOXy):
Ответ 3
Я не знаю, почему JAXB делает это - или даже если это JAXB - если JAXB выводит XML через SAXContentHandler, например, он не имеет прямого контроля над тем, как создаются теги close.
Чтобы получить последовательное поведение, вы можете обернуть свой OutputStream в OutputStreamWriter, например.
public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException {
try {
marshaller.marshal(obj, new OutputStreamWriter(os, "UTF-8"));
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
В одних и тех же строках вы можете увидеть, что произойдет, если вы обернете StringWriter в PrintWriter. Может быть, есть какой-то пользовательский код, который обнаруживает StringWriter
, чтобы попытаться максимально сократить вывод. Звучит маловероятно, но у меня нет другого объяснения.
Ответ 4
Почему это имеет значение? < tag attribute = "value" > </tag> эквивалентно < tag attribute = "value" /" > в xml