Нет @XmlRootElement, сгенерированного JAXB

Я пытаюсь создать классы Java из FpML (Finanial Products Markup Language) версии 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]

в самом деле нет classses имеют аннотацию @XmlRootElement, так что я могу делать неправильно?. Я указываю xjc (JAXB 2.1) на fpml-main-4-5.xsd, который затем включает все типы.

14 ответов


чтобы связать воедино то, что другие уже заявили или намекнули, правила, по которым JAXB XJC решает, ставить или нет @XmlRootElement аннотация на сгенерированном классе нетривиальна (эту статью).

@XmlRootElement существует, потому что среда выполнения JAXB требует определенной информации для маршалирования/unmarshal данного объекта, в частности имя элемента XML и пространство имен. Вы можете не только пройти любой старый объект в Маршаллер. @XmlRootElement предоставляет этого информация.

аннотация - это просто удобство, однако-JAXB не требует этого. Альтернативой является использование JAXBElement объекты-оболочки, которые предоставляют ту же информацию, что и @XmlRootElement, но и в виде объекта, а не аннотации.

, JAXBElement объекты неудобны для построения, так как вам нужно знать имя элемента XML и пространство имен, чего обычно не делает бизнес-логика.

к счастью, когда XJC генерирует модель класса, он также генерирует класс под названием ObjectFactory. Это отчасти для обратной совместимости с JAXB v1, но это также место для XJC, чтобы поместить сгенерированные заводские методы, которые создают JAXBElement обертки вокруг ваших собственных объектов. Он обрабатывает имя XML и пространство имен для вас, поэтому вам не нужно беспокоиться об этом. Вам просто нужно посмотреть через ObjectFactory методы (и для большой схемы их могут быть сотни), чтобы найти тот, который вам нужен.


Это упоминается в нижней части сообщения в блоге, уже связанного выше, но это работает как удовольствие для меня:

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);

<xsd:element name="myRootElement" type="MyRootElementType" />

<xsd:complexType name="MyRootElementType">
...
</xsd:complexType>

вы бы:

<xsd:element name="myRootElement">
    <xsd:complexType>
    ...
    <xsd:complexType>
</xsd:element>

@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.


ответ Джо (Джо июня 26 ' 09 В 17: 26) делает это для меня. Самый простой ответ заключается в том, что отсутствие @XmlRootElement аннотация-это не проблема, если вы маршал JAXBElement. То, что меня смутило, - это сгенерированный ObjectFactory имеет 2 метода createMyRootElement-первый не принимает параметров и дает развернутый объект, второй берет развернутый объект и возвращает его, завернутый в JAXBElement, и маршалинг, что JAXBElement работает нормально. Вот основной код, который я использовал (я новичок в это, поэтому извиняюсь, если код не отформатирован правильно в этом ответе), в основном cribbed от текст ссылки:

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;
  }
}

вы можете исправить эту проблему, используя привязку от как создать классы @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>

здесь контент

<?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>

Как вы знаете, ответ заключается в использовании 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();
    }

Это не работает для нас. Но мы нашли широко цитируемую статью, которая добавляет некоторый фон... Я свяжусь с ним здесь ради следующего человека:http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html


С помощью Maven, вы можете добавить @XmlRootElement аннотации

С "jaxb2-basics-annotate" плагин.

см. больше информации: см.

настройка Maven для создания классов из XML-схемы с помощью JAXB

и генерация кода JAXB XJC


в случае, если мой опыт этой проблемы дает кому-то Эврика! момент.. Добавлю следующее:

Я также получал эту проблему при использовании xsd-файла, который я создал с помощью опции меню IntelliJ "создать xsd из экземпляра документа".

когда я принял все значения по умолчанию этого инструмента, он сгенерировал xsd-файл, который при использовании с jaxb сгенерировал java-файлы без @XmlRootElement. Во время выполнения, когда я пытался маршалировать, я получил то же исключение, что и обсуждалось в этот вопрос.

Я вернулся к инструменту IntellJ и увидел опцию по умолчанию в раскрывающемся списке "Тип Desgin" (что, конечно, я не понял.. и все равно, если честно) было:

Тип Desgin:

"локальные элементы / глобальные сложные типы"

Я изменил это, чтобы

"локальные элементы/типа"

, теперь он сгенерировал (существенно) другой xsd, который произвел @XmlRootElement при использовании с jaxb. Не могу сказать, что понимаю, в чем тут дело, но у меня получилось.


вы пытались изменить свой xsd так?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>

фантики JAXBElement работает для случаев, когда нет @XmlRootElement генерируется JAXB. Эти обертки доступны в 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;
      }
 }

для решения этого вы должны настроить привязку 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>

после того, как sruggling в течение двух дней я нашел решение проблемы.Вы можете использовать ObjectFactory класс для обхода для классов, которые не имеют @XmlRootElement. ObjectFactory имеет перегруженные методы, чтобы обернуть его вокруг JAXBElement. Способ:1 совсем простое создание объекта и Способ:2 обернет объект с @JAXBElement. Всегда использовать Способ:2 чтобы избежать класса javax.XML.связывать.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();