没有由 JAXB 生成的 @XmlRootElement
Posted
技术标签:
【中文标题】没有由 JAXB 生成的 @XmlRootElement【英文标题】:No @XmlRootElement generated by JAXB 【发布时间】:2010-10-23 14:43:00 【问题描述】:我正在尝试从 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]
事实上 no 类有 @XmlRootElement 注释,所以我做错了什么?我将 xjc (JAXB 2.1) 指向 fpml-main-4-5.xsd,然后包含所有类型。
【问题讨论】:
【参考方案1】:为了将其他人已经陈述或暗示的内容联系在一起,JAXB XJC 决定是否将 @XmlRootElement
注释放在生成的类上的规则非常重要 (see this article)。
@XmlRootElement
存在是因为 JAXB 运行时需要某些信息才能编组/解组给定对象,特别是 XML 元素名称和命名空间。您不能将任何旧对象传递给 Marshaller。 @XmlRootElement
提供此信息。
注解只是为了方便,但是 - JAXB 不需要它。替代方法是使用JAXBElement
包装对象,它提供与@XmlRootElement
相同的信息,但以对象的形式,而不是注释。
但是,JAXBElement
对象很难构造,因为您需要知道 XML 元素名称和命名空间,而业务逻辑通常不需要。
幸运的是,当 XJC 生成一个类模型时,它也生成了一个名为 ObjectFactory
的类。这部分是为了向后兼容 JAXB v1,但它也是 XJC 放置生成的工厂方法的地方,这些方法在您自己的对象周围创建 JAXBElement
包装器。它为您处理 XML 名称和命名空间,因此您无需担心。您只需查看ObjectFactory
方法(对于大型架构,可能有数百个)找到您需要的方法。
【讨论】:
特殊情况解决方案:当您可以修改用于生成类的 xsd 时:阅读此答案中提供的链接后,我的解决方案是修改使用的 xsd 文件生成类:我将根元素的定义更改为内联定义,而不是使用对单独定义的类型的引用。这些允许 JAXB 将此元素设置为 @XmlRootElement,这对于之前用于根元素的 elementType 是不可能的。new ObjectFactory().createPositionReport(positionReport)
返回JAXBElement<PositionReport>
如果生成的 ObjectFactory 方法没有创建将参数包装在 JXBElement
中的方法怎么办?在我的例子中,工厂方法是 0-arity 并且只返回一个 new
对象。 (为什么有些类给定了 JAXBElement 包装器助手而其他类没有?)我想在这种情况下我们必须自己创建包装器?
@CarlG 我处于同样的情况 - 我的课程中没有出现 XmlRootElement 和 JAXBElement。你找到解决这个案子的办法了吗?【参考方案2】:
上面已经链接的博文底部提到了这一点,但这对我来说就像是一种享受:
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);
【讨论】:
我更喜欢标记的答案,但这也适用于我。 以上sn-p中的jc
是什么?
@ArunRaj 它是 JAXBContext 类【参考方案3】:
正如上述答案之一所暗示的,如果在 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 注释。有什么想法吗? 这也有助于你看到你的 ObjectFactory 生成的方法没有参数【参考方案4】:解组不需要@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 注释。
【讨论】:
您是否知道一种同样优雅的方式来编组没有 XmlRootElement 的对象 - 无需像 skaffman、Gurnard 等人提到的那样将其包装在 JAXBElement 中? +1 完美运行!为更清晰而进行一次编辑...在您的解决方案中,“someSource”是一个非常模糊的术语。详细说明:JAXBElementString pathname = "file.xml"; InputStream stream = new FileInputStream(pathname); JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader someSource = factory.createXMLEventReader(stream); JAXBElement<UserType> userElement = jaxbUnmarshaller.unmarshal(someSource, UserType.class); UserType user = userElement.getValue();
【参考方案5】:
您可以使用来自How to generate @XmlRootElement Classes for Base Types in XSD? 的绑定来解决此问题。
这是一个使用 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>
【讨论】:
确实,在 binding.xjb 文件中使用Joe 的回答(Joe 2009 年 6 月 26 日 17:26)为我做了。简单的答案是,如果您编组 JAXBElement,则没有 @XmlRootElement 注释是没有问题的。让我感到困惑的是,生成的 ObjectFactory 有 2 个 createMyRootElement 方法——第一个不带参数并给出未包装的对象,第二个采用未包装的对象并将其返回包装在 JAXBElement 中,并且编组 JAXBElement 可以正常工作。这是我使用的基本代码(我是新手,如果此回复中的代码格式不正确,请致歉),主要抄自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;
【讨论】:
我的 ObjectFactory 类只定义了返回常规实例而不是 JAXBElement 实例的方法...【参考方案7】:如您所知,答案是使用 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();
【讨论】:
到你的意思...我如何使用来自 ObjectFactory 的 JAXBElement> create...() 方法用于嵌套元素?即:折腾了两天,我找到了问题的解决方案。对于没有@XmlRootElement的类,你可以使用ObjectFactory类来解决。 ObjectFactory 具有将其包装在 JAXBElement 周围的重载方法。
Method:1 进行对象的简单创建。
Method:2 将使用 @JAXBElement 包装对象。
始终使用 Method:2 来避免 javax.xml.bind.MarshalException - 链接异常缺少 @XmlRootElement 注释。
请在下面找到示例代码
Method:1做简单的对象创建
public GetCountry createGetCountry()
return new GetCountry();
Method: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();
【讨论】:
感谢您使用 spring webservice 模板提供代码参考,因为很长一段时间都在努力解决这个问题!【参考方案9】:如果我对这个问题的经验给了某人一个 Eureka!时刻..我将添加以下内容:
当使用我使用 IntelliJ 的“从实例文档生成 xsd”菜单选项生成的 xsd 文件时,我也遇到了这个问题。
当我接受这个工具的所有默认值时,它会生成一个 xsd 文件,当与 jaxb 一起使用时,会生成没有 @XmlRootElement
的 java 文件。在运行时,当我尝试编组时,我遇到了与此问题中讨论的相同的异常。
我回到 IntellJ 工具,看到“设计类型”下拉菜单中的默认选项(我当然不明白.. 老实说仍然不明白)是:
设计类型:
“局部元素/全局复杂类型”
我把它改成了
“本地元素/类型”
,现在它生成了一个(基本上)不同的 xsd,它在与 jaxb 一起使用时生成了@XmlRootElement
。不能说我了解它的来龙去脉,但它对我有用。
【讨论】:
【参考方案10】:这对我们也不起作用。但是我们确实找到了一篇被广泛引用的文章,它添加了一些背景......我会在这里链接到它以供下一个人使用:http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html
【讨论】:
这对我很有效,谢谢。我还发现我在处理这个过程中编组了错误的 JAXB 对象(不是我想的根)。我忘记创建一个 JAXBElement 并试图仅编组从绑定中获得的 ObjectFactory 类中返回的对象。这基本上完全解决了这个问题(以防其他人遇到同样的问题)。 404 : "我们很抱歉 java.net 站点已关闭。以前托管在 java.net 上的大多数开源项目都已重新定位。" web.archive.org/web/20070812073809/http://weblogs.java.net/blog/…【参考方案11】:使用 Maven 构建,您可以添加 @XmlRootElement
注释
使用“jaxb2-basics-annotate
”插件。
查看更多信息:查看
Configure Maven to generate classes from XML Schema using JAXB
和JAXB XJC code generation
【讨论】:
【参考方案12】: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;
【讨论】:
【参考方案13】:你试过这样改变你的xsd吗?
<!-- create-logical-system -->
<xs:element name="methodCall">
<xs:complexType>
...
</xs:complexType>
</xs:element>
【讨论】:
这对我来说适用于 JDK 1.7u71。 xjc 为***元素分配了@XmlRootElement。最初我只有一个***复杂类型。必须包含在 JAXBElement 中是很丑陋的。【参考方案14】:这个话题已经很老了,但在企业业务环境中仍然相关。我尽量避免触摸 xsd,以便将来轻松更新它们。这是我的解决方案..
1。大多数情况下xjc:simple
就足够了
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc">
<jxb:globalBindings>
<xjc:simple/> <!-- adds @XmlRootElement annotations -->
</jxb:globalBindings>
</jxb:bindings>
它将主要创建用于导入 xsd 定义的 XmlRootElements。
2。划分你的 jaxb2-maven-plugin
处决
我遇到过,如果您尝试从多个 xsd 定义而不是每个 xsd 的执行定义生成类,这会产生巨大的差异。
因此,如果您有一个包含多个 <source>
的定义,请尝试拆分它们:
<execution>
<id>xjc-schema-1</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<xjbSources>
<xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
</xjbSources>
<sources>
<source>src/main/resources/xsd/definition1/</source>
</sources>
<clearOutputDir>false</clearOutputDir>
</configuration>
</execution>
<execution>
<id>xjc-schema-2</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<xjbSources>
<xjbSource>src/main/resources/xsd/binding.xjb</xjbSource>
</xjbSources>
<sources>
<source>src/main/resources/xsd/definition2/</source>
</sources>
<clearOutputDir>false</clearOutputDir>
</configuration>
</execution>
生成器不会捕捉到一个类可能就足够的事实,因此每次执行都会创建自定义类。这正是我需要的;)。
【讨论】:
【参考方案15】:要解决它,您应该在使用 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>
【讨论】:
包装标签应该是<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
【参考方案16】:
所以我使用 mavens maven-jaxb2-plugin
从一个大而复杂的 WSDL 文件生成类并遇到了这个问题。问题是 WSDL 中的元素将 complexType
定义引用为 type
,因此未生成元素类,并且在尝试使用 complexType 类时会产生缺少的 @XmlRootElement
错误。
在我看来,修改 WSDL 并不是一个真正可行的解决方案,唯一可行的方法似乎是设计一种在生成期间添加缺失注释的方法。它还会在编组时导致序列化问题,因为请求发送了错误的元素名称,并且响应中也没有具有匹配元素名称的类。
我最终做的是使用第二个 maven 插件jaxb2-basics-annotate
,它允许您通过使用 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/estimateServiceBinding.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>
生成的类 (GetQuickEstimateRequestContainer.java) 带有@XmlRootElement 注释和正确的元素名称
@XmlRootElement(name = "GetQuickEstimateRequest")
public class GetQuickEstimateRequestContainer
...
// class member fields & setters and getters
...
【讨论】:
【参考方案17】:我只是在同样的问题上挣扎了一段时间,只想发布我的最终结果,这对我来说很好。 所以基本问题是:
我必须从没有 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));
【讨论】:
以上是关于没有由 JAXB 生成的 @XmlRootElement的主要内容,如果未能解决你的问题,请参考以下文章