我试图从FpML(金融产品标记语言)4.5版本生成Java类。生成了大量代码,但我无法使用它。试图序列化一个简单的文档,我得到这个:

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,然后包括所有类型。


当前回答

因此,我使用mavens maven-jaxb2-plugin从一个大而复杂的WSDL文件生成类,并遇到了这个问题。问题是WSDL中的元素引用了complexType定义作为类型,因此没有生成元素类,并且在尝试使用complexType类时产生了缺少的@XmlRootElement错误。

在我看来,修改WSDL并不是一个切实可行的解决方案,唯一可行的方法似乎是设计一种在生成过程中添加缺失注释的方法。它还会在编组时引起序列化问题,因为请求发送了错误的元素名称,而且响应中也没有具有匹配元素名称的类。

最后我使用了第二个maven插件jaxb2-basic - annotation,它允许您通过使用jaxb绑定文件将缺少的注释添加到所需的类中。这使您不必在不添加不必要代码的情况下对这个问题进行排序,也意味着如果将来需要使用更新的WSDL文件,您可以轻松地重新生成。

pom.xml(注意,在配置中有一个插件部分用于执行- WSDL文件的位置是/src/main/resources/ WSDL /EstimatingService.wsdl)

<project>
    ...
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <jaxb2.version>0.14.0</jaxb2.version>
        <jaxb2.annotate.version>1.1.0</jaxb2.annotate.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>${jaxb2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics-annotate</artifactId>
            <version>${jaxb2.annotate.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>${jaxb2.version}</version>
                <executions>
                    <execution>
                        <id>estimating</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaLanguage>WSDL</schemaLanguage>
                            <generateDirectory>target/generated-sources/acme/src/gen/estimating-service</generateDirectory>
                            <generatePackage>com.acme.shipping.estimating.service</generatePackage>
                            <schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory>
                            <schemaIncludes>
                                <include>EstimatingService.wsdl</include>
                            </schemaIncludes>
                            <bindingDirectory>${project.basedir}/src/main/resources/bindings</bindingDirectory>
                            <bindingIncludes>estimateServiceBinding.xjb</bindingIncludes>
                            <extension>true</extension>
                            <args>
                                <arg>-Xannotate</arg>
                                <arg>-XremoveAnnotation</arg>
                            </args>
                            <plugins>
                                <plugin>
                                    <groupId>org.jvnet.jaxb2_commons</groupId>
                                    <artifactId>jaxb2-basics-annotate</artifactId>
                                </plugin>
                            </plugins>
                        </configuration>
                    </execution>
                    ...
                    // More executions here if you have multiple WSDL files (Dont forget to give it a different package name and id)
                </executions>
            </plugin>
        </plugins>
    </build>
    ...
</project>  

estimateServiceBinding。xjb(本例中使用的jaxb绑定文件- /src/main/resources/bindings/ estimateservicbbinding .xjb)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:annox="http://annox.dev.java.net" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <jaxb:globalBindings generateElementProperty="false">
        <xjc:simple />
    </jaxb:globalBindings>
    <!-- Target the schema section in the WSDL file using the given target namespace which contains the complexType definitions we want to annotate -->
    <jaxb:bindings schemaLocation="../wsdl/EstimatingService.wsdl" node="//xs:schema[@targetNamespace='http://acme.com/schema/datatypes/v2']">
        <jaxb:bindings node="xs:complexType[@name='GetQuickEstimateRequestContainer']">
            <!-- Add the @XmlRootElement annotation to the generated class and then tell it use the correct element name required when marshalling. e.g GetQuickEstimateRequestContainer element is renamed to the element name that referenced it in the WSDL (GetQuickEstimateRequest) -->
            <annox:annotateClass>@javax.xml.bind.annotation.XmlRootElement(name="GetQuickEstimateRequest")</annox:annotateClass>
        </jaxb:bindings>
        <jaxb:bindings node="xs:complexType[@name='GetQuickEstimateResponseContainer']">
            <annox:annotateClass>@javax.xml.bind.annotation.XmlRootElement(name="GetQuickEstimateResponse")</annox:annotateClass>
        </jaxb:bindings>
    </jaxb:bindings>
</jaxb:bindings>

生成带有@XmlRootElement注释和正确元素名称的类(GetQuickEstimateRequestContainer.java)

@XmlRootElement(name = "GetQuickEstimateRequest")
public class GetQuickEstimateRequestContainer {
    ...
    // class member fields & setters and getters
    ...
}

其他回答

正如上面的答案之一所暗示的,如果在XSD中根元素的类型定义为命名类型,则根元素上不会有XMLRootElement,因为该命名类型可以在XSD中的其他地方使用。尝试将其转换为匿名类型,即:

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

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

你会:

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

这对我们也没用。但我们确实找到了一篇被广泛引用的文章,增加了一些背景……我将在这里链接到它,以方便下一个人:http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

如您所知,答案是使用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();
    }

@XmlRootElement不需要解组-如果使用2参数形式的Unmarshaller#unmarshall。

所以,如果不这样做:

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

一个人应该做:

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

后一种代码将不需要UserType类级别的@XmlRootElement注释。

经过两天的努力,我找到了解决问题的办法。您可以使用ObjectFactory类来解决没有@XmlRootElement的类。ObjectFactory重载了一些方法来将其包装在JAXBElement周围。

方法:1简单地创建对象。

方法:2将使用@JAXBElement包装对象。

总是使用Method:2来避免javax.xml.bind.MarshalException -与链接异常缺少@XmlRootElement注释。

请查看下面的示例代码

方法:1简单地创建对象

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

方法:2将使用@JAXBElement包装对象。

 @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("test_guid");

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

GetCountryResponse response = jaxbResponse.getValue();