没有由JAXB生成的@XmlRootElement
题
我正在尝试从FpML(Finanial产品标记语言)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,然后包含所有类型。
解决方案
为了将其他人已经陈述或暗示过的内容联系在一起,JAXB XJC决定是否将 @XmlRootElement
注释放在生成的类上的规则是非常重要的(请参阅此文)。
@XmlRootElement
,因为JAXB运行时需要某些信息才能编组/解组给定对象,特别是XML元素名称和命名空间。你不能只将任何旧物体传递给Marshaller。 @XmlRootElement
提供此信息。
注释只是一种便利,但JAXB并不需要它。替代方法是使用 JAXBElement
包装器对象,它提供与 @XmlRootElement
相同的信息,但是以对象的形式,而不是注释。
但是, JAXBElement
对象构造起来很难,因为您需要知道XML元素名称和命名空间,而业务逻辑通常不知道。
值得庆幸的是,当XJC生成类模型时,它还会生成一个名为 ObjectFactory
的类。这部分是为了向后兼容JAXB v1,但它也是XJC放置生成的工厂方法的地方,这些方法在您自己的对象周围创建 JAXBElement
包装器。它为您处理XML名称和命名空间,因此您无需担心它。您只需要查看 ObjectFactory
方法(对于大型模式,可以有数百个)来查找所需的方法。
其他提示
这已在上面已经链接的博客文章的底部提到,但这对我来说很有用:
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);
正如在上面的一个答案中暗示的那样,如果在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>
解组时不需要@XmlRootElement - 如果使用Unmarshaller#unmarshall的2参数形式。
所以,如果不是这样做:
UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));
应该做的事情:
JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();
后一个代码在UserType类级别不需要@XmlRootElement注释。
Joe的回答(Joe Jun 26 '09 at 17:26)为我做了这件事。简单的答案是,如果编组JAXBElement,则缺少@XmlRootElement注释是没有问题的。让我感到困惑的是生成的ObjectFactory有2个createMyRootElement方法 - 第一个不接受任何参数并给出解包的对象,第二个接受解包的对象并返回包装在JAXBElement中,并编组JAXBElement工作正常。这是我使用的基本代码(我是新手,如果代码在此回复中没有正确格式化,请道歉),主要来自链接文字:
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>
这是 binding.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>
如您所知,答案是使用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();
}
它也不适用于我们。但我们确实找到了一篇广泛引用的文章,增加了一些背景......为了下一个人的缘故,我会链接到这里: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html
如果我对此问题的体验给某人一个尤里卡!那一刻..我将添加以下内容:
当使用我使用IntelliJ的“从实例文档生成xsd”生成的xsd文件时,我也遇到了这个问题。菜单选项。
当我接受了这个工具的所有默认值时,它生成了一个xsd文件,当与jaxb一起使用时,生成的java文件没有 @XmlRootElement
。在运行时,当我试图编组时,我遇到了与此问题中讨论的相同的异常。
我回到了IntellJ工具,看到了“Desgin Type”中的默认选项。下降(当然我不理解......如果我是诚实的话,仍然没有)是:
设计类型:
“本地元素/全球复杂类型”
我将此更改为
&quot; local elements / types&quot;
,现在它生成了一个(基本上)不同的xsd,当与jaxb一起使用时产生 @XmlRootElement
。不能说我理解它的内部和外部,但它对我有用。
使用Maven构建,您可以添加 @XmlRootElement
注释
使用“ jaxb2-basics-annotate
”插件。
查看更多信息:见
您是否尝试过改变这样的xsd?
<!-- create-logical-system -->
<xs:element name="methodCall">
<xs:complexType>
...
</xs:complexType>
</xs:element>
JAXBElement包装器适用于JAXB不生成 @XmlRootElement
的情况。这些包装器在 maven-jaxb2-plugin
生成的 ObjectFactory
类中可用。例如:
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;
}
}
经过两天的努力后,我找到了问题的解决方案。您可以使用 ObjectFactory 类来解决没有 @XmlRootElement 的类的问题。 ObjectFactory有重载方法将它包装在JAXBElement周围。 方法:1 执行对象的简单创建,方法:2 将使用 @JAXBElement 包装对象。始终使用方法:2 来避免javax.xml.bind.MarshalException - 链接异常缺少@XmlRootElement注释
方法:1
public GetCountry createGetCountry() {
return new GetCountry();
}
方式:2 强>
@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("1f3e1771-3049-49f5-95e6-dc3732c3227b");
JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));
GetCountryResponse response = jaxbResponse.getValue();
要解决它,你应该在使用wsimport进行编译之前配置一个xml绑定,将generateElementProperty设置为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>