Вопрос

Я пытаюсь сгенерировать классы 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, который затем включает в себя все типы.

Это было полезно?

Решение

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

@XmlRootElement существует, потому что среда выполнения JAXB требует определенной информации для маршалинга / демаршализации данного объекта, в частности имени элемента 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);

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

@XmlRootElement не требуется для демаршаллинга - если используется форма с 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 июня 2009 в 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;
  }
}

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

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

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

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

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

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

Тип дизайна:

  

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

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

  

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

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

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

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

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

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

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

Вы пытались изменить свой xsd следующим образом?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>
Оболочки

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

После двухдневных усилий я нашел решение проблемы.Вы можете использовать Объектный завод класс для обхода для классов, у которых нет @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();

Чтобы решить эту проблему, вы должны настроить привязку 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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top