我试图从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
...
}
Joe's answer (Joe Jun 26 '09 at 17:26) does it for me. The simple answer is that absence of an @XmlRootElement annotation is no problem if you marshal a JAXBElement. The thing that confused me is the generated ObjectFactory has 2 createMyRootElement methods - the first takes no parameters and gives the unwrapped object, the second takes the unwrapped object and returns it wrapped in a JAXBElement, and marshalling that JAXBElement works fine. Here's the basic code I used (I'm new to this, so apologies if the code's not formatted correctly in this reply), largely cribbed from link text:
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;
}
}
您可以使用如何在XSD中为基类型生成@XmlRootElement类中的绑定来修复此问题。
下面是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>
这是装订。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>
我只是在同一个问题上挣扎了一段时间,只是想发布我的最终结果,这对我来说很好。
所以基本问题是:
我必须从没有XmlRootElement注释的JAXB类实例生成xml字符串
这些类需要为编组过程绑定其他类
下面的类可以很好地解决这个问题:
public class Object2XmlConverter {
public static <T> String convertToString(final T jaxbInstance, final Class<?>... additionalClasses)
throws JAXBException {
final Class<T> clazz = (Class<T>) jaxbInstance.getClass();
final JAXBContext jaxbContext;
if (additionalClasses.length > 0) {
// this path is only necessary if you need additional classes to be bound
jaxbContext = JAXBContext.newInstance(addClassesToBeBound(clazz, additionalClasses));
} else {
jaxbContext = JAXBContext.newInstance(clazz);
}
final QName qname = new QName("", jaxbInstance.getClass().getSimpleName());
final JAXBElement<T> jaxbElement = new JAXBElement<T>(qname, clazz, null, jaxbInstance);
final Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
final StringWriter stringWriter = new StringWriter();
jaxbMarshaller.marshal(jaxbElement, stringWriter);
return stringWriter.toString();
}
private static <T> Class<?>[] addClassesToBeBound(final Class<T> clazz, final Class<?>[] additionalClasses) {
final Class<?>[] classArray = new Class<?>[additionalClasses.length + 1];
for (int i = 0; i < additionalClasses.length; i++) {
classArray[i] = additionalClasses[i];
}
classArray[classArray.length - 1] = clazz;
return classArray;
}
public static void main(final String[] args) throws Exception {
final Ns1TargetHeaderTyp dataTyp = ...;
System.out.println(convertToString(dataTyp));
}
}