没有由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,然后包含所有types。
为了将其他人已经陈述或暗示的东西联系在一起,JAXB XJC决定是否将@XmlRootElement
注释放在生成的类上的规则并不是微不足道的( 参见本文 )。
存在@XmlRootElement
是因为JAXB运行时需要某些信息来编组/给一个给定对象,尤其是XML元素名称和命名空间。 你不能把任何旧的物体传递给Marshaller。 @XmlRootElement
提供了这个信息。
注释只是一个方便,但是 – JAXB不需要它。 另一种方法是使用JAXBElement
包装器对象,它提供与@XmlRootElement
相同的信息,但采用对象的forms,而不是注释。
但是,由于您需要知道XML元素名称和命名空间,所以业务逻辑通常不会知道JAXBElement
对象是不是很容易构造。
谢天谢地,当XJC生成类模型时,它也会生成一个名为ObjectFactory
的类。 这在一定程度上是为了向后兼容JAXB v1,但是它也可以作为XJC放置生成的工厂方法的地方,这些工厂方法在您自己的对象周围创buildJAXBElement
包装器。 它为你处理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中将其types定义为命名types,则不会在根元素上获得XMLRootElement,因为可以在XSD中的其他位置使用该命名types。 尝试使用匿名types,而不是:
<xsd:element name="myRootElement" type="MyRootElementType" /> <xsd:complexType name="MyRootElementType"> ... </xsd:complexType>
你将会拥有:
<xsd:element name="myRootElement"> <xsd:complexType> ... <xsd:complexType> </xsd:element>
如果使用Unmarshaller#unmarshall的2参数forms,则不需要@XmlRootElement解组。
所以,如果不这样做:
UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));
应该这样做:
JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class); UserType user = userElement.getValue();
后面的代码将不需要在UserType类级别的@XmlRootElement注释。
乔的回答(乔Jun 29 '09在17:26)为我做。 简单的答案是,如果编组JAXBElement,则缺less@XmlRootElement批注是没有问题的。 让我感到困惑的是生成的ObjectFactory有两个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中的基本types生成@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>
它也不适合我们。 但是我们确实find了一个被广泛引用的文章,增加了一些背景知识……为了下一个人的目的,我将在这里链接到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(); }
使用Maven构build,您可以添加@XmlRootElement
注释
带有“ jaxb2-basics-annotate
”插件。
查看更多信息:请参阅
configurationMaven使用JAXB从XML Schema生成类
和JAXB XJC代码生成
你有没有尝试像这样改变你的xsd?
<!-- create-logical-system --> <xs:element name="methodCall"> <xs:complexType> ... </xs:complexType> </xs:element>
如果我的这个问题的经验给人一个尤里卡! 时刻..我会添加以下内容:
当我使用IntelliJ的“从实例文档生成xsd”菜单选项生成的xsd文件时,我也遇到了这个问题。
当我接受这个工具的所有默认值时,它生成一个xsd文件,当与jaxb一起使用时,生成不带@XmlRootElement的java文件。 在运行时,当我尝试编组时,得到了与此问题中讨论的相同的exception。
我回到了IntellJ工具,看到“Desgin Type”下拉菜单中的默认选项(当然,我不明白,但是如果我诚实的话,我还是不这样做)是:
devisetypes:
“本地元素/全球复杂types”
我改变了这个
“本地元素/types”
,现在它生成了一个(基本上)不同的xsd,与jaxb @XmlRootElement
使用时生成了@XmlRootElement。 我不能说我明白了它的内在和外在,但它对我有效。
为了解决它,你应该configuration一个xml绑定,然后用wsimport进行编译,将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>
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; } }