Нет @XmlRootElement, созданного JAXB - PullRequest
195 голосов
/ 04 мая 2009

Я пытаюсь сгенерировать 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]

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

Ответы [ 14 ]

251 голосов
/ 31 января 2010

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

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

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

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

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

65 голосов
/ 03 мая 2011

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

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);
47 голосов
/ 27 февраля 2013

Как указывалось в одном из приведенных выше ответов, вы не получите 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>
34 голосов
/ 09 августа 2011

@ XmlRootElement не требуется для демаршаллинга - если используется форма с двумя параметрами Unmarshaller # unmarshall.

Итак, если вместо этого:

UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));

нужно сделать:

JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();

Последний код не требует аннотации @XmlRootElement на уровне класса UserType.

20 голосов
/ 30 декабря 2010

Ответ Джо (Joe Jun 26 '09 в 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;
  }
}
17 голосов
/ 16 мая 2012

Эту проблему можно исправить, используя привязку из Как создать классы @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 голосов
/ 18 февраля 2016

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

У нас это тоже не работает. Но мы нашли широко цитируемую статью, в которой добавлено НЕКОТОРОЕ предысторию ... Я приведу ссылку на нее здесь ради следующего человека: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

5 голосов
/ 26 ноября 2014

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

Эта проблема также возникала при использовании файла xsd, созданного с помощью параметра меню IntelliJ «Создать документ xsd из документа экземпляра».

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

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

Тип дизайна:

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

Я изменил это на

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

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

4 голосов
/ 07 июня 2012

В сборке Maven вы можете добавить @XmlRootElement аннотацию

с плагином "jaxb2-basics-annotate".

Дополнительная информация: см.

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

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

...