我试图从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
...
}
@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注释。
因此,我使用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
...
}
万一我对这个问题的经验给了某人一个顿悟!时刻. .我补充如下:
在使用IntelliJ的“从实例文档中生成xsd”菜单选项生成的xsd文件时,我也遇到了这个问题。
当我接受该工具的所有默认值时,它生成了一个xsd文件,当与jaxb一起使用时,生成不带@XmlRootElement的java文件。在运行时,当我试图marshal时,我得到了与这个问题中讨论的相同的异常。
我回到IntellJ工具,在“设计类型”下拉菜单中看到了默认选项(当然我不明白…老实说,我现在也不知道)是:
设计类型:
“局部元素/全局复杂类型”
我把这个改为
“local文本/ types”
,现在它生成了一个(实质上)不同的xsd,当与jaxb一起使用时,生成了@XmlRootElement。我不能说我理解它的来龙去脉,但它对我很管用。