Как маршалировать объект через JAXB без какой-либо информации об этом?
У меня есть объект value
, который имеет какой-то тип, либо @XmlRootElement
-незаменит, либо нет. Я хочу объединить его в XML:
String value1 = "test";
assertEquals("<foo>test</foo>", toXml("foo", value1));
// ...
@XmlRootElement
class Bar {
public String bar = "test";
}
assertEquals("<foo><bar>test</bar></foo>", toXml("foo", new Bar()));
Могу ли я сделать это с существующими средствами JAXB, или я должен создать какой-то пользовательский анализатор?
Ответы
Ответ 1
Вы можете использовать JAXBIntrospector для выполнения следующих действий:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
Object value = "Hello World";
//Object value = new Bar();
JAXBContext jc = JAXBContext.newInstance(String.class, Bar.class);
JAXBIntrospector introspector = jc.createJAXBIntrospector();
Marshaller marshaller = jc.createMarshaller();
if(null == introspector.getElementName(value)) {
JAXBElement jaxbElement = new JAXBElement(new QName("ROOT"), Object.class, value);
marshaller.marshal(jaxbElement, System.out);
} else {
marshaller.marshal(value, System.out);
}
}
@XmlRootElement
public static class Bar {
}
}
При использовании вышеуказанного кода при сортировке JAXBElement он будет иметь атрибут xsi: type, соответствующий соответствующему типу схемы:
<ROOT
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Hello World</ROOT>
Чтобы исключить квалификацию, вы можете просто изменить строку, которая создает JAXBElement, чтобы:
JAXBElement jaxbElement = new JAXBElement(new QName("ROOT"), value.getClass(), value);
Это приведет к следующему XML:
<ROOT>Hello World</ROOT>
Ответ 2
Вот как маршал value1
, который является String
. Вы можете передать yourObject.getClass()
в конструктор JAXBElement
и value1
:
try {
JAXBContext jc = JAXBContext.newInstance();
Marshaller m = jc.createMarshaller();
String value1 = "test";
JAXBElement jx = new JAXBElement(new QName("foo"), value1.getClass(), value1);
m.marshal(jx, System.out);
} catch (JAXBException ex) {
ex.printStackTrace();
}
Это работает без использования @XmlRootElement
. Результатом приведенного выше кода было:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo>test</foo>
С другой стороны, это не будет работать с объектом Bar
: javax.xml.bind.JAXBException: myPackage.Bar is not known to this context
. Однако вы можете получить значение изнутри Bar
и создать JAXBElement
с этим, а не с самим объектом.
Ответ 3
Я не нашел хорошего общего способа сделать это. Вот мое общее решение.
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
public class XMLConverter {
/**
* Serialize object to XML string
* @param object object
* @param <T> type
* @return
*/
public static <T> String marshal(T object) {
try {
StringWriter stringWriter = new StringWriter();
JAXBContext jc = JAXBContext.newInstance(object.getClass());
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
QName qName = new QName(object.getClass().getCanonicalName(), object.getClass().getSimpleName());
JAXBElement<T> root = new JAXBElement(qName, object.getClass(), object);
m.marshal(root, stringWriter);
return stringWriter.toString();
} catch (Exception e) {
// log the exception
}
return null;
}
/**
* Deserialize XML string back to object
* @param content XML content
* @param clasz class
* @param <T> type
* @return
*/
public static <T> T unMarshal(final String content, final Class<T> clasz) {
try {
JAXBContext jc = JAXBContext.newInstance(clasz);
Unmarshaller u = jc.createUnmarshaller();
return u.unmarshal(new StreamSource(new StringReader(content)), clasz).getValue();
} catch (Exception e) {
// log the exception
}
return null;
}
}
Ответ 4
Если он не аннотируется с помощью @XmlRootElement
, то JAXB не имеет достаточной информации для его маршалирования. Сначала вам нужно обернуть его в JAXBElement
.
Не могли бы вы задуматься, как обернуть объект в соответствующем JAXBElement
?