Когда JAXB unmarshaller.unmarshal возвращает JAXBElement<MySchemaObject> или MySchemaObject?
У меня есть два кода в двух разных Java-проектах, выполняющих почти одно и то же, (отменяя сортировку ввода веб-сервиса в соответствии с xsd-файлом).
Но в одном случае я должен написать это: (Input является именем заполнителя) (element является вводом OMElement)
ClassLoader clInput = input.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("input", clInput);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() );
а в другой lib я должен использовать JAXBElement.getValue(), потому что это JAXBElement, который возвращается, и простое приведение (Input) просто завершается сбоем:
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() ).getValue();
Знаете ли вы, что приводит к такой разнице?
5 ответов
Если корневой элемент однозначно соответствует классу Java, будет возвращен экземпляр этого класса, а если нет JAXBElement
будет возвращен.
Если вы хотите, чтобы вы всегда получали экземпляр объекта домена, вы можете использовать JAXBInstrospector
, Ниже приведен пример.
демонстрация
package forum10243679;
import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
private static final String XML = "<root/>";
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBIntrospector jaxbIntrospector = jc.createJAXBIntrospector();
Object object = unmarshaller.unmarshal(new StringReader(XML));
System.out.println(object.getClass());
System.out.println(jaxbIntrospector.getValue(object).getClass());
Object jaxbElement = unmarshaller.unmarshal(new StreamSource(new StringReader(XML)), Root.class);
System.out.println(jaxbElement.getClass());
System.out.println(jaxbIntrospector.getValue(jaxbElement).getClass());
}
}
Выход
class forum10243679.Root
class forum10243679.Root
class javax.xml.bind.JAXBElement
class forum10243679.Root
Это зависит от наличия аннотации XmlRootElement на классе вашего корневого элемента.
Если вы генерируете свои классы JAXB из XSD, применяются следующие правила:
- если тип корневого элемента является анонимным - в сгенерированный класс добавляется аннотация XmlRootElement
- если тип корневого элемента относится к типу верхнего уровня -> аннотация XmlRootElement исключается из сгенерированного класса
По этой причине я часто выбираю анонимные типы для корневых элементов.
Вы можете настроить имя класса этого анонимного типа с помощью файла настроек. Например, создайте файл bindings.xjc следующим образом:
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings schemaLocation="yourXsd.xsd" node="/xs:schema">
<jxb:bindings node="//xs:element[@name='yourRootElement']">
<jxb:class name="YourRootElementType"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Вам нужно добавить в свой сгенерированный класс JAXB @XMLRootElement
- должно иметь пространство имен:
@XmlRootElement(namespace="http://your.namespace.com/", name="yourRootElement")
Посмотрите на связанный вопрос (есть много хороших советов): исключение класса Cast при попытке демонтировать xml?
Изменение сгенерированных классов Java, я не согласен. Не позволяя все возможные форматы XSD, я не согласен.
Спасибо всем вашим объяснениям ссылки, это код, который я написал, чтобы позаботиться об обоих случаях, используя Annotation Introspection. Он работает как для вывода, так и для ввода, и (на мой вкус) более общий:
public class JaxbWrapper {
private static boolean isXmlRootElement(Class classT){
Annotation[] annotations = classT.getAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof XmlRootElement){
return true;
}
}
return false;
}
public static Object unmarshall(Class classObjectFactory, Class classObject, XMLStreamReader xmlStreamReader){
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
Object returnObject = null;
try {
JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
Unmarshaller unmarshaller = jc.createUnmarshaller();
returnObject = unmarshaller.unmarshal( xmlStreamReader );
boolean bIsRootedElement = isXmlRootElement(classObject);
if(!bIsRootedElement)
{
JAXBElement jaxbElement = (JAXBElement) returnObject;
returnObject = jaxbElement.getValue();
}
}
catch (JAXBException e) {
/*...*/
}
return returnObject;
}
private static void writeToXml(Class classObjectFactory, Object obj, XMLStreamWriter xmlStreamWriter){
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
try {
JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(obj, xmlStreamWriter);
}
catch(JAXBException e) {
/*...*/
}
}
public static String marshall(Class classObjectFactory, Class classObject, Object obj){
Object objectToMarshall = obj;
boolean bIsRootedElement = isXmlRootElement(classObject);
if(!bIsRootedElement)
{
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
String strClassName = classObject.getName();
QName qName = new QName(strPackageName, strClassName);
JAXBElement jaxbElement = new JAXBElement(qName, classObject, null, obj);
objectToMarshall = jaxbElement;
}
StringWriter sw = new StringWriter();
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xmlStreamWriter = null;
try {
xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw);
writeToXml(classObjectFactory, objectToMarshall, xmlStreamWriter);
xmlStreamWriter.flush();
xmlStreamWriter.close();
}
catch (XMLStreamException e) {
/*...*/
}
return sw.toString();
}
}
У меня та же проблема. JAXB unmarshaller.unmarshal возвращает JAXBElement<MyObject>
вместо желаемого MyObject
,
Я нашел и удалил @XmlElementDecl
, Проблема решена.