Không có @XmlRootE bổ sung được tạo bởi JAXB


209

Tôi đang cố gắng tạo các lớp Java từ FpML (Ngôn ngữ đánh dấu sản phẩm Finanial) phiên bản 4.5. Một tấn mã được tạo ra, nhưng tôi không thể sử dụng nó. Cố gắng nối tiếp một tài liệu đơn giản tôi nhận được điều này:

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]

Trong thực tế, không có lớp nào có chú thích @XmlRootEuity, vậy tôi có thể làm gì sai?. Tôi đang trỏ xjc (JAXB 2.1) vào fpml-main-4-5.xsd, sau đó bao gồm tất cả các loại.

Câu trả lời:


261

Để gắn kết những gì người khác đã nêu hoặc gợi ý, các quy tắc mà JAXB XJC quyết định có hay không đưa @XmlRootElementchú thích vào một lớp được tạo là không tầm thường ( xem bài viết này ).

@XmlRootElementtồn tại bởi vì thời gian chạy JAXB yêu cầu một số thông tin nhất định để sắp xếp / sắp xếp một đối tượng nhất định, cụ thể là tên và không gian tên phần tử XML. Bạn không thể chuyển bất kỳ vật cũ nào cho Marshaller. @XmlRootElementcung cấp thông tin này.

Tuy nhiên, chú thích chỉ là một sự tiện lợi - JAXB không yêu cầu nó. Cách khác là sử dụng JAXBElementcác đối tượng trình bao bọc, cung cấp thông tin giống như @XmlRootElement, nhưng ở dạng đối tượng, thay vì chú thích.

Tuy nhiên, JAXBElementcác đối tượng rất khó xây dựng, vì bạn cần biết tên và không gian tên phần tử XML, điều mà logic nghiệp vụ thường không có.

Rất may, khi XJC tạo ra một mô hình lớp, nó cũng tạo ra một lớp được gọi ObjectFactory. Đây là một phần để tương thích ngược với JAXB v1, nhưng đây cũng là nơi để XJC đặt các phương thức nhà máy được tạo để tạo các JAXBElementhàm bao quanh các đối tượng của riêng bạn. Nó xử lý tên và không gian tên XML cho bạn, vì vậy bạn không cần phải lo lắng về nó. Bạn chỉ cần xem qua các ObjectFactoryphương thức (và đối với lược đồ lớn, có thể có hàng trăm trong số chúng) để tìm ra phương thức bạn cần.


15
Giải pháp trường hợp đặc biệt: khi bạn có thể sửa đổi xsd được sử dụng để tạo lớp: Sau khi đọc liên kết được cung cấp trong câu trả lời này, giải pháp trong trường hợp của tôi là sửa đổi tệp xsd được sử dụng để tạo các lớp: Tôi đã thay đổi định nghĩa của phần tử gốc thành một định nghĩa nội tuyến thay vì sử dụng tham chiếu đến một loại được xác định riêng biệt. Điều này cho phép JAXB đặt phần tử này là @XmlRootEuity, điều này không thể thực hiện được với phần tửType đã được sử dụng trước đó cho phần tử gốc.
Arthur

2
Tuy nhiên, <scowl> thay đổi phần tử gốc thành loại nội tuyến, làm cho tất cả các lớp trở thành các lớp bên trong của loại gốc. Ngoài ra, ngay cả khi loại phần tử gốc được xác định SAU chính phần tử gốc (rõ ràng được cho phép bởi lược đồ), JAXB vẫn sẽ không chú thích với @XmlRootEuity.
Pawel Veselov

10
tức là new ObjectFactory().createPositionReport(positionReport)trả lạiJAXBElement<PositionReport>
vikingsteve

17
Điều gì xảy ra nếu phương thức ObjectFactory được tạo không tạo ra một phương thức bao bọc đối số trong một JXBElement? Trong trường hợp của tôi, phương thức nhà máy là 0-arity và chỉ trả về một newđối tượng. (Tại sao một số lớp được cung cấp trợ giúp trình bao bọc JAXBEuity và các lớp khác thì không?) Tôi đoán trong trường hợp đó chúng ta phải tự tạo trình bao bọc?
Carl G

1
@CarlG Tôi đang ở trong tình trạng tương tự - không có XmlRootEuity hay JAXBEuity xuất hiện trong các lớp học của tôi. Bạn đã tìm thấy một giải pháp cho trường hợp này?
Mickael Marrache

68

Điều này được đề cập ở dưới cùng của bài viết blog đã được liên kết ở trên nhưng điều này hoạt động như một điều trị cho tôi:

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

Tôi thích câu trả lời được đánh dấu, nhưng điều này cũng làm việc cho tôi.
Pedro Dusso

1
những gì jctrong đoạn trích trên?
Arun

3
@ArunRaj đó là lớp JAXBContext
Gurnard

51

Như được gợi ý trong một trong những câu trả lời ở trên, bạn sẽ không nhận được XMLRootE bổ sung cho phần tử gốc của mình nếu trong XSD, loại của nó được định nghĩa là một loại được đặt tên, vì loại có tên đó có thể được sử dụng ở nơi khác trong XSD của bạn. Hãy thử mking nó một loại ẩn danh, tức là thay vì:

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

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

bạn sẽ có:

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

1
Điều đó không đúng với tôi. Loại của tôi là ẩn danh (được nhúng bên trong phần tử gốc của tôi) và không có chú thích XmlRootE bổ sung nào được tạo. Bất kỳ ý tưởng?
Mickael Marrache 23/2/2016

38

@XmlRootEuity không cần thiết cho việc sắp xếp lại - nếu ai đó sử dụng dạng 2 tham số của Unmarshaller # unmarshall.

Vì vậy, nếu thay vì làm:

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

người ta nên làm:

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

Mã sau sẽ không yêu cầu chú thích @XmlRootE bổ sung ở cấp lớp UserType.


2
Bạn có biết một cách thanh lịch không kém để sắp xếp một đối tượng không có XmlRootEuity - mà không gói nó trong một JAXBEuity như được đề cập bởi skaffman, Gurnard et al?
Chris

4
+1 Hoạt động hoàn hảo! Một chỉnh sửa cho rõ ràng hơn ... Trong giải pháp của bạn, 'someSource' là thuật ngữ rất mơ hồ. Để giải thích: JAXBEuity <TargetClazz> root = unmarshaller.unmarshal (StreamSource mới (Tệp mới ("some.xml")), TargetClazz. Class);
siêu tân tinh

4
Công phu hơn nữa của '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 Pitchers

21

Câu trả lời của Joe (Joe Jun 26 '09 lúc 17:26) làm điều đó cho tôi. Câu trả lời đơn giản là sự vắng mặt của một chú thích @XmlRootEuity sẽ không có vấn đề gì nếu bạn sắp xếp một JAXBEuity. Điều làm tôi bối rối là ObjectFactory được tạo có 2 phương thức createMyRootEuity - lần đầu tiên không có tham số nào và cung cấp cho đối tượng chưa được bao bọc, lần thứ hai lấy đối tượng chưa được bao bọc và trả về nó được bọc trong JAXBEuity và xử lý JAXBEuity hoạt động tốt. Đây là mã cơ bản mà tôi đã sử dụng (Tôi chưa quen với điều này, vì vậy xin lỗi nếu mã không được định dạng chính xác trong câu trả lời này), phần lớn được viết từ văn bản liên kết :

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
Tôi có một trường hợp trong đó lớp ObjectFactory của tôi chỉ định nghĩa các phương thức trả về các thể hiện thông thường chứ không phải các thể hiện JAXBEuity ...
Mickael Marrache 23/2/2016

20

Bạn có thể khắc phục sự cố này bằng cách sử dụng liên kết từ Cách tạo lớp @XmlRootE bổ sung cho các loại cơ sở trong XSD? .

Dưới đây là một ví dụ với 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>

Đây là binding.xjbnội dung tập tin

<?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
Thật vậy, sử dụng <xjc: simple> trong tệp bind.xjb đã thực hiện thủ thuật này. Giải pháp tuyệt vời nếu bạn không muốn thay đổi mã sắp xếp hoặc WSDL của mình. Lưu ý rằng xjc: đơn giản tạo ra các tên phương thức khác nhau (số nhiều) cho các bộ sưu tập (ví dụ getOrder thay vì getOrder)
dvtoever

10

Như bạn đã biết câu trả lời là sử dụng ObjectFactory (). Đây là một mẫu mã làm việc cho tôi :)

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

theo quan điểm của bạn ... làm cách nào để tôi sử dụng các phương thức JAXBEuity <?> tạo ... () từ ObjectFactory cho các phần tử lồng nhau? tức là: <SOAP-ENV: Header> <wsse: Security> <wsse: UsernameToken> </ wsse: UsernameToken> </ wsse: Security> </ SOAP-ENV: Header> Tôi nhận được: "không thể soái ca" là một yếu tố vì nó thiếu một chú thích @XmlRootEuity "
Angelina

6

Nó cũng không làm việc cho chúng tôi. Nhưng chúng tôi đã tìm thấy một bài viết được trích dẫn rộng rãi có thêm MỘT SỐ nền tảng ... Tôi sẽ liên kết đến nó ở đây vì lợi ích của người tiếp theo: http://weblogs.java.net/blog/kohsuke/archive/2006/03 /why_does_jaxb_p.html


Điều này làm việc tốt cho tôi, cảm ơn bạn. Tôi cũng thấy rằng tôi đã sắp xếp sai đối tượng JAXB (không phải gốc như tôi nghĩ) trong quá trình trải qua điều này. Tôi đã quên tạo ra một JAXBEuity và đang cố gắng sắp xếp lại đối tượng được trả về từ lớp ObjectFactory mà tôi đã thu được từ liên kết. Điều này về cơ bản đã quan tâm đến vấn đề hoàn toàn (trong trường hợp bất kỳ ai khác gặp phải vấn đề tương tự).
Joe Bane

1
404: "Chúng tôi xin lỗi, trang java.net đã đóng cửa. Hầu hết các dự án Nguồn mở được lưu trữ trước đó trên java.net đã được di dời."
Tristan


6

Sau khi thử nghiệm hai ngày, tôi đã tìm ra giải pháp cho vấn đề này. Bạn có thể sử dụng lớp ObjectFactory để giải quyết cho các lớp không có @XmlRootEuity . ObjectFactory đã quá tải các phương thức để bọc nó xung quanh JAXBEuity.

Phương pháp: 1 thực hiện việc tạo đơn giản của đối tượng.

Phương thức: 2 sẽ bọc đối tượng bằng @JAXBEuity .

Luôn sử dụng Phương thức: 2 để tránh javax.xml.bind.MarshalException - với ngoại lệ được liên kết thiếu một chú thích @XmlRootEuity.

Vui lòng tìm mã mẫu bên dưới

Phương thức: 1 thực hiện đơn giản việc tạo đối tượng

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

Phương thức: 2 sẽ bọc đối tượng bằng @JAXBEuity .

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

Mẫu mã làm việc:

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

Cảm ơn bạn đã cung cấp tham chiếu mã với mẫu dịch vụ web mùa xuân như đã đấu tranh để tìm ra nó trong một thời gian khá lâu!
RRR_J

5

Trong trường hợp kinh nghiệm của tôi về vấn đề này mang lại cho ai đó một Eureka! Khoảnh khắc .. Tôi sẽ thêm vào như sau:

Tôi cũng gặp vấn đề này, khi sử dụng tệp xsd mà tôi đã tạo bằng tùy chọn trình đơn "Tạo xsd từ tài liệu InstanceJ" của IntelliJ.

Khi tôi chấp nhận tất cả các giá trị mặc định của công cụ này, nó đã tạo một tệp xsd mà khi được sử dụng với jaxb, đã tạo các tệp java không có @XmlRootElement. Trong thời gian chạy khi tôi cố gắng làm nguyên soái, tôi đã có ngoại lệ giống như đã thảo luận trong câu hỏi này.

Tôi đã quay lại công cụ IntellJ và thấy tùy chọn mặc định trong danh sách "Loại ký quỹ" (tất nhiên tôi không hiểu .. và vẫn không biết nếu tôi trung thực) là:

Loại ký quỹ:

"các yếu tố địa phương / Các loại phức tạp toàn cầu"

Tôi đã thay đổi điều này thành

"các yếu tố / loại địa phương"

, bây giờ nó đã tạo ra một xsd (thực chất) khác nhau, tạo ra @XmlRootElementkhi được sử dụng với jaxb. Không thể nói tôi hiểu được những thứ trong và ngoài của nó, nhưng nó hiệu quả với tôi.



4

Các trình bao bọc JAXBEuity hoạt động cho các trường hợp không @XmlRootElementđược tạo bởi JAXB. Các hàm bao có sẵn trong ObjectFactorylớp được tạo bởi maven-jaxb2-plugin. Ví dụ:

     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

Bạn đã thử thay đổi xsd của bạn như thế này?

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

Điều này làm việc cho tôi với JDK 1.7u71. Một phần tử cấp cao nhất được gán cho @XmlRootEuity bởi xjc. Ban đầu tôi chỉ có một loại phức tạp cấp cao nhất. Phải bọc trong một JAXBEuity là xấu xí.
Serge Merzliakov

1

Để giải quyết nó, bạn nên cấu hình một ràng buộc xml trước để biên dịch với wsimport, đặt GenerEuityProperty là 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>

thẻ gói phải là<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
aliopi

0

Chủ đề khá cũ nhưng vẫn có liên quan trong bối cảnh kinh doanh doanh nghiệp. Tôi đã cố gắng tránh chạm vào xsds để dễ dàng cập nhật chúng trong tương lai. Đây là giải pháp của tôi ..

1. Chủ yếu xjc:simplelà đủ

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

Nó chủ yếu sẽ tạo XmlRootElements để nhập định nghĩa xsd.

2. Phân chia các jaxb2-maven-pluginvụ hành quyết của bạn

Tôi đã gặp phải rằng nó tạo ra sự khác biệt lớn nếu bạn cố gắng tạo các lớp từ nhiều định nghĩa xsd thay vì định nghĩa thực thi trên mỗi xsd.

Vì vậy, nếu bạn có một định nghĩa với nhiều <source>', hơn là chỉ cố gắng phân tách chúng:

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

Trình tạo sẽ không nắm bắt được thực tế là một lớp có thể đủ và do đó tạo các lớp tùy chỉnh cho mỗi lần thực hiện. Và đó chính xác là những gì tôi cần;).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.