没有JAXB生成的@XmlRootElement


209

我正在尝试从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,其中包括所有类型。

Answers:


261

为了将其他人已经声明或暗示的内容结合在一起,JAXB XJC决定是否将@XmlRootElement注释放在生成的类上的规则并不简单(请参见本文)。

@XmlRootElement之所以会存在,是因为JAXB运行时需要某些信息才能封送/取消封送给定的对象,特别是XML元素名称和名称空间。您不能仅将任何旧对象传递给Marshaller。@XmlRootElement提供此信息。

注释只是一种方便,但是-JAXB不需要它。替代方法是使用JAXBElement包装器对象,该对象提供与相同的信息@XmlRootElement,但以对象的形式而不是注释的形式。

然而, JAXBElement对象的构造很麻烦,因为您需要知道XML元素名称和名称空间,而业务逻辑通常不知道。

幸运的是,当XJC生成类模型时,它还生成了一个名为的类ObjectFactory。这样做的部分原因是为了与JAXB v1向后兼容,但是XJC还是在这里放置了生成的工厂方法,这些方法JAXBElement围绕您自己的对象创建包装器。它为您处理XML名称和名称空间,因此您无需担心。您只需要查看这些ObjectFactory方法(对于大型模式,可能有数百种方法)来找到所需的方法。


15
特例解决方案:当您可以修改用于类生成的xsd时: 阅读此答案中提供的链接后,我的情况下的解决方案是修改用于生成类的xsd文件:我将根元素的定义更改为内联定义,而不是使用对分别定义的类型的引用。这些允许JAXB将此元素设置为@XmlRootElement,而对于以前用于根元素的elementType则无法实现。
亚瑟

2
<scowl>将根元素更改为内联类型,但是会使所有类成为根类型的内部类。同样,即使在根元素本身之后定义了根元素类型(显然由架构允许),JAXB仍不会使用@XmlRootElement进行注释。
Pawel Veselov

10
new ObjectFactory().createPositionReport(positionReport)返回JAXBElement<PositionReport>
vikingsteve

17
如果生成的ObjectFactory方法未创建将参数包装为的方法JXBElement怎么办?在我的情况下,factory方法为0-arity,并且仅返回一个new对象。(为什么有些类没有给JAXBElement包装器助手,而其他类却没有?)我想在这种情况下,我们必须自己创建包装器?
卡尔·G

1
@CarlG我处于相同的情况-我的班级中没有XmlRootElement或JAXBElement出现。您是否找到这种情况的解决方案?
米切尔·马拉奇

68

在上面已链接的博客文章的底部提到了此问题,但这对我来说就像一种享受:

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);

我更喜欢标记的答案,但这也对我有用。
Pedro Dusso 2014年

1
是什么jc在上面的代码片段?
阿伦

3
@ArunRaj是JAXBContext类
Gurnard

51

如上述答案之一所示,如果在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>

1
对我来说不对。我的类型是匿名的(嵌入在我的根元素中)并且没有生成XmlRootElement批注。任何想法?
Mickael Marrache '16

38

解组不需要@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批注。


2
您是否知道一种同样优雅的方法来封送不具有XmlRootElement的对象-不用像skaffman,Gurnard等人提到的那样将其包装在JAXBElement中?
克里斯(Chris

4
+1完美运作!为了更清晰起见,请进行一次编辑...在您的解决方案中,“ someSource”是一个非常模糊的术语。详细说明:JAXBElement <TargetClazz> root = unmarshaller.unmarshal(new StreamSource(new File(“ some.xml”)),TargetClazz.class);
2013年

4
“ someSource”的详细说明:String 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();
Steve

21

乔的答案(乔9年6月26日在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;
  }
}

1
我遇到的情况是,我的ObjectFactory类仅定义返回常规实例而不返回JAXBElement实例的方法……
Mickael Marrache

20

您可以使用如何为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>

3
实际上,在binding.xjb文件中使用<xjc:simple>可以达到目的。如果您不想更改您的封送处理代码或WSDL,则是很棒的解决方案。请注意,xjc:simple为集合getter(例如,用getOrders代替getOrder)生成不同的方法名称(复数)
dvtoever 2014年

10

众所周知,答案是使用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 ...()方法来嵌套元素?即:<SOAP-ENV:Header> <wsse:Security> <wsse:UsernameToken> </ wsse:UsernameToken> </ wsse:Security> </ SOAP-ENV:Header>我得到:“无法编组类型“ UsernameTokenType”作为元素,因为它缺少@XmlRootElement注释”
Angelina

6

这对我们也不起作用。但是我们确实找到了一篇引述广泛的文章,该文章增加了一些背景知识。为了便于下一个人,我将在此处链接到该文章:http : //weblogs.java.net/blog/kohsuke/archive/2006/03 /why_does_jaxb_p.html


这对我来说效果很好,谢谢。我还发现,在经历此过程时,我正在整理错误的JAXB对象(不是我想的根)。我忘了创建一个JAXBElement,并试图封送我从绑定获得的ObjectFactory类中返回的对象。这基本上完全解决了这个问题(以防其他人遇到相同的问题)。
乔·班恩

1
404:“很抱歉,java.net站点已关闭。以前托管在java.net上的大多数开放源代码项目都已重定位。”
特里斯坦(Tristan)


6

经过两天的摸索,我找到了解决问题的方法。您可以使用ObjectFactory类来解决没有@XmlRootElement的类。。ObjectFactory具有重载的方法来将其包装在JAXBElement周围。

方法:1进行对象的简单创建。

方法:2将使用@JAXBElement包装对象

始终使用Method:2来避免javax.xml.bind.MarshalException-链接的异常缺少@XmlRootElement批注。

请在下面找到示例代码

方法:1进行对象的简单创建

public GetCountry createGetCountry() {
        return new GetCountry();
    }

方法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模板中提供代码参考,因为花了很多时间才努力解决!
$$

5

万一我对这个问题的经验给某人一个尤里卡!时刻..我将添加以下内容:

当使用通过IntelliJ的“从实例文档生成xsd”菜单选项生成的xsd文件时,我也遇到了这个问题。

当我接受该工具的所有默认设置时,它会生成一个xsd文件,与jaxb一起使用时,会生成no的java文件@XmlRootElement。在运行时,当我尝试进行封送时,遇到了与该问题所讨论的异常相同的异常。

我回到IntellJ工具,并在“ Desgin Type”下拉菜单中看到了默认选项(当然,我不理解,但我还是不知道),它是:

设计类型:

“本地元素/全局复杂类型”

我将其更改为

“本地元素/类型”

,现在它生成了一个(基本上)不同的xsd,@XmlRootElement当与jaxb一起使用时会生成the 。不能说我了解它的来龙去脉,但是它对我有用。



4

JAXBElement包装器适用于@XmlRootElementJAXB 不生成的情况。这些包装器可在ObjectFactory生成的类中使用maven-jaxb2-plugin。例如:

     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;
      }
 }

3

您是否尝试过这样更改xsd?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>

这对JDK 1.7u71来说对我有用。xjc为顶层元素分配了@XmlRootElement。最初,我只有顶级复杂类型。必须包装JAXBElement是很丑陋的。
Serge Merzliakov 2015年

1

为了解决这个问题,您应该先配置一个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>

包装标签应为<jaxb:bindings> ... <jaxws:bindings> ... </jaxws:bindings> ... </jaxb:bindings>
aliopi

0

这个主题很老,但在企业业务环境中仍然有意义。我试图避免触摸xsds,以便将来轻松更新它们。这是我的解决方案。

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>

生成器不会捕捉到一个类可能已足够的事实,因此每次执行都会创建自定义类。那正是我所需要的;)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.