Nie wygenerowano @XmlRootElement przez JAXB


209

Próbuję wygenerować klasy Java z FpML (Finanial Products Markup Language) wersja 4.5. Wygenerowano tonę kodu, ale nie mogę go użyć. Próbuję serializować prosty dokument, otrzymuję to:

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]

W rzeczywistości żadna klasa nie ma adnotacji @XmlRootElement, więc co mogę zrobić źle ?. Wskazuję xjc (JAXB 2.1) na fpml-main-4-5.xsd, który następnie obejmuje wszystkie typy.

Odpowiedzi:


261

Aby powiązać to, co inni już stwierdzili lub podpowiedzieli, zasady, według których JAXB XJC decyduje, czy umieścić @XmlRootElementadnotację w wygenerowanej klasie, nie są trywialne ( zobacz ten artykuł ).

@XmlRootElementistnieje, ponieważ środowisko wykonawcze JAXB wymaga pewnych informacji w celu uporządkowania / odmarszowania danego obiektu, w szczególności nazwy elementu XML i przestrzeni nazw. Nie możesz po prostu przekazać Marshallerowi żadnego starego obiektu. @XmlRootElementzapewnia tę informację.

Adnotacja to jednak tylko wygoda - JAXB jej nie wymaga. Alternatywą jest użycie JAXBElementobiektów opakowania, które dostarczają tych samych informacji co @XmlRootElement, ale w formie obiektu, a nie adnotacji.

Jednak JAXBElementobiekty są trudne do zbudowania, ponieważ musisz znać nazwę elementu XML i przestrzeń nazw, czego zwykle nie zna logika biznesowa.

Na szczęście, gdy XJC generuje model klasy, generuje również klasę o nazwie ObjectFactory. Ma to częściowo na celu zapewnienie kompatybilności wstecznej z JAXB v1, ale jest także miejscem, w którym XJC może umieszczać wygenerowane metody fabryczne, które tworzą JAXBElementopakowania wokół twoich obiektów. Obsługuje dla Ciebie nazwę XML i przestrzeń nazw, więc nie musisz się o to martwić. Wystarczy przejrzeć ObjectFactorymetody (w przypadku dużego schematu mogą istnieć setki), aby znaleźć tę, której potrzebujesz.


15
Rozwiązanie przypadku specjalnego: kiedy można zmodyfikować plik xsd używany do generowania klas: po przeczytaniu łącza podanego w tej odpowiedzi rozwiązaniem w moim przypadku była modyfikacja pliku xsd użytego do wygenerowania klas: zmieniłem definicję elementu głównego na wstawiona definicja zamiast odwoływania się do typu zdefiniowanego osobno. Dzięki temu JAXB może ustawić ten element jako @XmlRootElement, co nie było możliwe w przypadku elementu elementType, który był wcześniej używany dla elementu głównego.
Arthur

2
<scowl> zmiana elementu głównego na wbudowany powoduje, że wszystkie klasy są wewnętrznymi klasami typu głównego. Ponadto, nawet jeśli typ elementu głównego jest zdefiniowany PO samym elemencie głównym (najwyraźniej dozwolonym przez schemat), JAXB nadal nie będzie adnotował @XmlRootElement.
Paweł Veselov

10
tzn. new ObjectFactory().createPositionReport(positionReport)zwrotyJAXBElement<PositionReport>
vikingsteve

17
Co jeśli wygenerowana metoda ObjectFactory nie tworzy metody, która otacza argument JXBElement? W moim przypadku metoda fabryczna to 0-arity i po prostu zwraca newobiekt. (Dlaczego niektóre klasy otrzymują pomocników opakowania JAXBElement, a inne nie?) Chyba w takim przypadku musimy sami stworzyć opakowanie?
Carl G

1
@CarlG Jestem w tej samej sytuacji - w moich klasach nie ma XmlRootElement ani JAXBElement. Czy znalazłeś rozwiązanie tej sprawy?
Mickael Marrache,

68

Wspomniano o tym na dole wpisu na blogu, do którego link już powyżej, ale działa to jak dla mnie przyjemność:

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

Wolę zaznaczoną odpowiedź, ale to też działa dla mnie.
Pedro Dusso,

1
co jest jcw powyższym fragmencie?
Arun

3
@ArunRaj to klasa JAXBContext
Gurnard

51

Jak wskazano w jednej z powyższych odpowiedzi, nie otrzymasz XMLRootElement dla elementu głównego, jeśli w XSD jego typ jest zdefiniowany jako nazwany typ, ponieważ ten nazwany typ może być użyty gdzie indziej w twoim XSD. Spróbuj użyć tego typu anonimowego, tzn. Zamiast:

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

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

miałbyś:

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

1
To nie jest dla mnie prawda. Mój typ jest anonimowy (osadzony w elemencie głównym) i adnotacja XmlRootElement nie jest generowana. Dowolny pomysł?
Mickael Marrache

38

@XmlRootElement nie jest potrzebny do odblokowania - jeśli używa się 2-parametrowej postaci Unmarshaller # unmarshall.

Więc jeśli zamiast robić:

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

należy zrobić:

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

Ten ostatni kod nie będzie wymagał adnotacji @XmlRootElement na poziomie klasy UserType.


2
Czy znasz równie elegancki sposób organizowania obiektu, który nie ma XmlRootElement - bez zawijania go w JAXBElement, o czym wspominali skaffman, Gurnard i in.?
Chris

4
+1 działa idealnie! Jedna edycja dla większej przejrzystości ... W twoim rozwiązaniu „someSource” jest bardzo niejasnym terminem. Aby rozwinąć: JAXBElement <TargetClazz> root = unmarshaller.unmarshal (nowy StreamSource (nowy plik („some.xml”)), TargetClazz.class);
supernowa

4
Dalsze opracowanie „someSource”:String pathname = "file.xml"; InputStream stream = new FileInputStream(pathname); JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader someSource = factory.createXMLEventReader(stream); JAXBElement<UserType> userElement = jaxbUnmarshaller.unmarshal(someSource, UserType.class); UserType user = userElement.getValue();
Steve

21

Odpowiedź Joe (Joe 26 czerwca 09 o 17:26) robi to dla mnie. Prosta odpowiedź jest taka, że ​​brak adnotacji @XmlRootElement nie stanowi problemu, jeśli utworzysz JAXBElement. Rzeczą, która mnie pomyliła, jest to, że ObjectFactory ma 2 metody createMyRootElement - pierwszy nie przyjmuje parametrów i daje rozpakowany obiekt, drugi bierze rozpakowany obiekt i zwraca go zawinięty w JAXBElement, a marshalling, że JAXBElement działa dobrze. Oto podstawowy kod, którego użyłem (jestem nowy, więc przepraszam, jeśli kod nie jest poprawnie sformatowany w tej odpowiedzi), w dużej mierze skopiowany z tekstu linku :

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

1
Mam przypadek, w którym moja klasa ObjectFactory definiuje tylko metody zwracające regularne instancje, a nie instancje JAXBElement ...
Mickael Marrache

20

Możesz rozwiązać ten problem, używając powiązania z Jak wygenerować klasy @XmlRootElement dla typów podstawowych w XSD? .

Oto przykład z 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>

Oto binding.xjbtreść pliku

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

3
Rzeczywiście, użycie <xjc: simple> w pliku bind.xjb załatwiło sprawę. Niesamowite rozwiązanie, jeśli nie chcesz zmieniać kodu zestawienia ani kodu WSDL. Zauważ, że xjc: simple generuje różne nazwy metod (liczba mnoga) dla pobierających kolekcje (na przykład getOrders zamiast getOrder)
dvtoever

10

Jak wiadomo odpowiedzią jest użycie ObjectFactory (). Oto próbka kodu, który zadziałał dla mnie :)

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

do rzeczy ... jak używać JAXBElement <?> metody tworzenia ... () z ObjectFactory dla elementów zagnieżdżonych? tj.: <SOAP-ENV: Nagłówek> <wsse: Bezpieczeństwo> <wsse: Nazwa_użytkownika> </ wsse: Nazwa_użytkownika> </ wsse: Bezpieczeństwo> </ SOAP-ENV: Nagłówek> Otrzymuję: „nie można wpisać typu„ UsernameTokenType ” jako element, ponieważ brakuje w nim adnotacji @XmlRootElement ”
Angelina

6

To też nie działa dla nas. Ale znaleźliśmy szeroko cytowany artykuł, który dodaje NIEKTÓRE tło ... Link do niego tutaj ze względu na następną osobę: http://weblogs.java.net/blog/kohsuke/archive/2006/03 /why_does_jaxb_p.html


To zadziałało dla mnie dobrze, dziękuję. Odkryłem również, że w trakcie przechodzenia przez to, że organizowałem niewłaściwy obiekt JAXB (nie root, jak myślałem). Zapomniałem utworzyć JAXBElement i próbowałem zgromadzić tylko zwrócony obiekt z klasy ObjectFactory, który uzyskałem z wiązania. To w zasadzie całkowicie rozwiązało problem (na wypadek, gdyby ktoś napotkał ten sam problem).
Joe Bane,

1
404: „Przykro nam, że witryna java.net została zamknięta. Większość projektów Open Source wcześniej hostowanych na java.net została przeniesiona.”
Tristan


6

Po dwóch dniach przeszukiwania znalazłem rozwiązanie problemu. Możesz użyć klasy ObjectFactory do obejścia klas, które nie mają @XmlRootElement . ObjectFactory ma przeciążone metody, aby zawinąć ją wokół JAXBElement.

Metoda: 1 wykonuje proste utworzenie obiektu.

Metoda: 2 zawinie obiekt w @JAXBElement .

Zawsze używaj metody: 2, aby uniknąć wyjątku javax.xml.bind.MarshalException - z połączonym wyjątkiem brakuje adnotacji @XmlRootElement.

Poniżej znajduje się przykładowy kod

Metoda: 1 wykonuje proste utworzenie obiektu

public GetCountry createGetCountry() {
        return new GetCountry();
    }

Metoda: 2 zawinie obiekt w @JAXBElement .

 @XmlElementDecl(namespace = "my/name/space", name = "getCountry")
 public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
        return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
    }

Przykładowy kod roboczy:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebServiceTemplate springWSTemplate = context.getBean(WebServiceTemplate.class);

GetCountry request = new GetCountry();
request.setGuid("test_guid");

JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));

GetCountryResponse response = jaxbResponse.getValue();

Dziękujemy za podanie odwołania do kodu z wiosennym szablonem usługi sieciowej, ponieważ przez długi czas próbowałem to rozgryźć!
RRR_J

5

Na wypadek, gdyby moje doświadczenie z tym problemem dało komuś Eurekę! moment .. Dodam następujące:

Ten problem również występował, gdy korzystałem z pliku xsd, który wygenerowałem przy pomocy opcji menu „Generuj xsd z dokumentu wystąpienia”.

Kiedy zaakceptowałem wszystkie domyślne ustawienia tego narzędzia, wygenerował on plik xsd, który przy użyciu z jaxb wygenerował pliki java bez @XmlRootElement. W czasie wykonywania, gdy próbowałem marszałka, dostałem ten sam wyjątek, co omówiony w tym pytaniu.

Wróciłem do narzędzia IntellJ i zobaczyłem domyślną opcję w rozwijanym „Desgin Type” (czego oczywiście nie rozumiałem… i nadal nie rozumiem, jeśli jestem szczery):

Desgin Type:

„elementy lokalne / typy globalnych kompleksów”

Zmieniłem to na

„elementy / typy lokalne”

, teraz wygenerował (zasadniczo) inny xsd, który wytworzył, @XmlRootElementgdy był używany z jaxb. Nie mogę powiedzieć, że rozumiem, co to jest, ale działało dla mnie.



4

Opakowania JAXBElement działają w przypadkach, w których @XmlRootElementJAXB nie generuje żadnego . Te opakowania są dostępne w ObjectFactoryklasie generowanej przez maven-jaxb2-plugin. Na przykład:

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

3

Czy próbowałeś zmienić swój xsd w ten sposób?

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

Działa to dla mnie z JDK 1.7u71. Elementowi najwyższego poziomu przypisano @XmlRootElement przez xjc. Początkowo miałem tylko typ kompleksu najwyższego poziomu. Konieczność owinięcia JAXBElement jest po prostu brzydka.
Serge Merzliakov

1

Aby to rozwiązać, przed skonfigurowaniem kompilacji z programem wsimport należy skonfigurować powiązanie xml, ustawiając opcję replaceElementProperty na wartość 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>

etykieta owijająca powinna być<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
aliopi

0

Temat jest dość stary, ale nadal aktualny w kontekście biznesowym przedsiębiorstwa. Starałem się unikać dotykania xsds, aby łatwo je zaktualizować w przyszłości. Oto moje rozwiązania ..

1. Przeważnie xjc:simplewystarcza

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    jxb:extensionBindingPrefixes="xjc">

    <jxb:globalBindings>
        <xjc:simple/> <!-- adds @XmlRootElement annotations -->
    </jxb:globalBindings>

</jxb:bindings>

Tworzy głównie XmlRootElements do importowania definicji xsd.

2. Podziel swoje jaxb2-maven-pluginegzekucje

Spotkałem się, że robi to ogromną różnicę, jeśli spróbujesz wygenerować klasy z wielu definicji xsd zamiast definicji wykonania dla xsd.

Więc jeśli masz definicję wielokrotności <source>, spróbuj po prostu podzielić je:

          <execution>
            <id>xjc-schema-1</id>
            <goals>
              <goal>xjc</goal>
            </goals>
            <configuration>
              <xjbSources>
                <xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
              </xjbSources>
              <sources>
                <source>src/main/resources/xsd/definition1/</source>
              </sources>
              <clearOutputDir>false</clearOutputDir>
            </configuration>
          </execution>

          <execution>
            <id>xjc-schema-2</id>
            <goals>
              <goal>xjc</goal>
            </goals>
            <configuration>
              <xjbSources>
                <xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
              </xjbSources>
              <sources>
                <source>src/main/resources/xsd/definition2/</source>
              </sources>
              <clearOutputDir>false</clearOutputDir>
            </configuration>
          </execution>

Generator nie wychwytuje faktu, że jedna klasa może być wystarczająca, a zatem tworzy niestandardowe klasy dla każdego wykonania. I właśnie tego potrzebuję;).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.