Нет @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>

Ответ 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();
    }

Ответ 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();