Нет @XmlRootElement, сгенерированный JAXB
Я пытаюсь создать классы Java из FpML (язык разметки продуктов) версии 4.5. Создается тонна кода, но я не могу его использовать. Пытаясь сериализовать простой документ, я получаю следующее:
javax.xml.bind.MarshalException
- with linked exception: [com.sun.istack.SAXException2: unable
to marshal type
"org.fpml._2008.fpml_4_5.PositionReport"
as an element because it is missing an
@XmlRootElement annotation]
Фактически no имеет аннотацию @XmlRootElement, поэтому что я могу делать неправильно?. Я указываю xjc (JAXB 2.1) на fpml-main-4-5.xsd, который затем включает все типы.
Ответы
Ответ 1
Чтобы связать то, о чем уже говорили или намекают другие, правила, по которым JAXB XJC решает, стоит ли добавлять аннотацию @XmlRootElement
к сгенерированному классу, нетривиальны (см. эту статью).
@XmlRootElement
существует, потому что время выполнения JAXB требует определенной информации, чтобы маршалировать/развязать данный объект, в частности, имя элемента XML и пространство имен. Вы не можете просто передать старый объект Маршаллеру. @XmlRootElement
предоставляет эту информацию.
Аннотации - это просто удобство, однако JAXB этого не требует. Альтернативой является использование объектов-оболочек JAXBElement
, которые предоставляют ту же информацию, что и @XmlRootElement
, но в виде объекта, а не аннотации.
Однако объекты JAXBElement
неудобны для построения, так как вам нужно знать имя и пространство имен XML, чего обычно нет в бизнес-логике.
К счастью, когда XJC генерирует модель класса, он также генерирует класс с именем ObjectFactory
. Отчасти это связано с обратной совместимостью с JAXB v1, но также и местом XJC для создания сгенерированных методов factory, которые создают обертки JAXBElement
вокруг ваших собственных объектов. Он обрабатывает имя XML и пространство имен для вас, поэтому вам не нужно беспокоиться об этом. Вам просто нужно просмотреть методы ObjectFactory
(и для большой схемы, их могут быть сотни), чтобы найти тот, который вам нужен.
Ответ 2
Это упоминается в нижней части сообщения блога, уже связанного выше, но это работает как удовольствие для меня:
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(new JAXBElement<MyClass>(new QName("uri","local"), MyClass.class, myClassInstance), System.out);
Ответ 3
Как указано в одном из приведенных выше ответов, вы не получите XMLRootElement в корневом элементе, если в XSD его тип определяется как именованный тип, так как этот именованный тип можно использовать в другом месте вашего XSD. Попробуйте ввести анонимный тип, т.е. Вместо:
<xsd:element name="myRootElement" type="MyRootElementType" />
<xsd:complexType name="MyRootElementType">
...
</xsd:complexType>
у вас будет:
<xsd:element name="myRootElement">
<xsd:complexType>
...
<xsd:complexType>
</xsd:element>
Ответ 4
@XmlRootElement не требуется для unmarshalling - если вы используете 2-мерную форму Unmarshaller # unmarshall.
Итак, если вместо этого:
UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));
нужно делать:
JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();
Последний код не требует аннотации @XmlRootElement на уровне класса UserType.
Ответ 5
Ответ на Joe (Joe Jun), 09:26 в 17:26) делает это для меня. Простой ответ заключается в том, что отсутствие аннотации @XmlRootElement не проблема, если вы маршалируете JAXBElement. То, что меня смутило, - это сгенерированный объект ObjectFactory имеет 2 метода createMyRootElement - первый не принимает параметров и предоставляет развернутый объект, второй берет разворачиваемый объект и возвращает его в JAXBElement и сортирует JAXBElement отлично. Вот базовый код, который я использовал (я новичок в этом, поэтому приношу извинения, если код не отформатирован правильно в этом ответе), в значительной степени скрещен из текст ссылки:
ObjectFactory objFactory = new ObjectFactory();
MyRootElement root = objFactory.createMyRootElement();
...
// Set root properties
...
if (!writeDocument(objFactory.createMyRootElement(root), output)) {
System.err.println("Failed to marshal XML document");
}
...
private boolean writeDocument(JAXBElement document, OutputStream output) {
Class<?> clazz = document.getValue().getClass();
try {
JAXBContext context =
JAXBContext.newInstance(clazz.getPackage().getName());
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(document, output);
return true;
} catch (JAXBException e) {
e.printStackTrace(System.err);
return false;
}
}
Ответ 6
Вы можете исправить эту проблему, используя привязку из Как создать классы @XmlRootElement для базовых типов в XSD?.
Вот пример с Maven
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>src/main/resources/xsd</schemaDirectory>
<packageName>com.mycompany.schemas</packageName>
<bindingFiles>bindings.xjb</bindingFiles>
<extension>true</extension>
</configuration>
</plugin>
Вот содержимое файла binding.xjb
<?xml version="1.0"?>
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings schemaLocation="path/to/myschema.xsd" node="/xs:schema">
<jxb:globalBindings>
<xjc:simple/>
</jxb:globalBindings>
</jxb:bindings>
</jxb:bindings>
Ответ 7
Он тоже не работает для нас. Но мы нашли широко цитируемую статью, в которой добавлен некоторый фон... Я свяжусь с ней здесь ради следующего человека: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html
Ответ 8
Как вы знаете, ответ заключается в использовании ObjectFactory(). Вот пример кода, который работал у меня:)
ObjectFactory myRootFactory = new ObjectFactory();
MyRootType myRootType = myRootFactory.createMyRootType();
try {
File file = new File("./file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(MyRoot.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
//output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JABXElement<MyRootType> myRootElement = myRootFactory.createMyRoot(myRootType);
jaxbMarshaller.marshal(myRootElement, file);
jaxbMarshaller.marshal(myRootElement, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
Ответ 9
С помощью сборки Maven вы можете добавить аннотацию @XmlRootElement
с плагином < <21 > .
См. дополнительную информацию: см.
Настроить Maven для создания классов из схемы XML с помощью JAXB
и генерация кода JAXB XJC
Ответ 10
Вы пытались изменить свой xsd следующим образом?
<!-- create-logical-system -->
<xs:element name="methodCall">
<xs:complexType>
...
</xs:complexType>
</xs:element>
Ответ 11
Если мой опыт этой проблемы дает кому-то Эврика! момент.. Я добавлю следующее:
Я тоже получал эту проблему при использовании xsd файла, который я сгенерировал, используя опцию меню "Создать файл xsd из экземпляра" в IntelliJ.
Когда я принял все значения по умолчанию этого инструмента, он сгенерировал файл xsd, который при использовании с jaxb генерировал java файлы без @XmlRootElement
. Во время выполнения, когда я пытался маршалировать, я получил то же исключение, что и в этом вопросе.
Я вернулся к инструменту IntellJ и увидел вариант по умолчанию в раскрывающемся списке "Desgin Type" (который, конечно же, я не понимал.. и до сих пор не знаю, если честно):
Тип Desgin:
"локальные элементы/глобальные сложные типы
Я изменил это на
"локальные элементы/типы"
теперь он сгенерировал (по существу) разные xsd, которые произвели @XmlRootElement
при использовании с jaxb. Не могу сказать, что я понимаю, в чем и из этого, но это сработало для меня.
Ответ 12
Оболочки JAXBElement работают в случаях, когда JAXB не генерирует @XmlRootElement
. Эти обертки доступны в классе ObjectFactory
, сгенерированном maven-jaxb2-plugin
. Например,
public class HelloWorldEndpoint {
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "person")
@ResponsePayload
public JAXBElement<Greeting> sayHello(@RequestPayload JAXBElement<Person> request) {
Person person = request.getValue();
String greeting = "Hello " + person.getFirstName() + " " + person.getLastName() + "!";
Greeting greet = new Greeting();
greet.setGreeting(greeting);
ObjectFactory factory = new ObjectFactory();
JAXBElement<Greeting> response = factory.createGreeting(greet);
return response;
}
}
Ответ 13
Чтобы решить проблему, вы должны настроить привязку xml перед компиляцией с помощью wsimport, установив generateElementProperty как false.
<jaxws:bindings wsdlLocation="LOCATION_OF_WSDL"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='NAMESPACE_OF_WSDL']">
<jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xjc:generateElementProperty>false</xjc:generateElementProperty>
</jxb:globalBindings>
</jaxws:bindings>
</jaxws:bindings>
Ответ 14
После того, как я боролся в течение двух дней, я нашел решение проблемы. Вы можете использовать класс ObjectFactory для обхода для классов, у которых нет @XmlRootElement. ObjectFactory перегружает методы, чтобы обернуть его вокруг JAXBElement. Метод: 1 делает простое создание объекта и Метод: 2 обертывает объект с помощью @JAXBElement. Всегда использовать Метод: 2, чтобы избежать javax.xml.bind.MarshalException - со связанным исключением отсутствует аннотация @XmlRootElement
Метод: 1
public GetCountry createGetCountry() {
return new GetCountry();
}
Метод: 2
@XmlElementDecl(namespace = "my/name/space", name = "getCountry")
public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
}
Пример рабочего кода:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebServiceTemplate springWSTemplate = context.getBean(WebServiceTemplate.class);
GetCountry request = new GetCountry();
request.setGuid("1f3e1771-3049-49f5-95e6-dc3732c3227b");
JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));
GetCountryResponse response = jaxbResponse.getValue();