没有由JAXB生成的@XmlRootElement

时间:2009-05-04 11:32:20

标签: java jaxb xjc fpml

我正在尝试从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,然后包含所有类型。

16 个答案:

答案 0 :(得分:247)

为了将其他人已经陈述或暗示过的内容联系在一起,JAXB XJC决定是否将@XmlRootElement注释放在生成的类上的规则是非常重要的(see this article)。 / p>

@XmlRootElement存在是因为JAXB运行时需要某些信息才能编组/解组给定对象,特别是XML元素名称和命名空间。你不能只将任何旧物体传递给Marshaller。 @XmlRootElement提供此信息。

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

但是,JAXBElement对象构造起来很难,因为您需要知道XML元素名称和命名空间,而业务逻辑通常不知道。

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

答案 1 :(得分:65)

这在上面已经链接的博客文章的底部提到,但这对我来说很有用:

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

答案 2 :(得分:47)

正如在上面的一个答案中暗示的那样,如果在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>

答案 3 :(得分:34)

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

答案 4 :(得分:20)

Joe的回答(Joe Jun 26 '09 at 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;
  }
}

答案 5 :(得分:17)

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

答案 6 :(得分: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();
    }

答案 7 :(得分:6)

它也不适用于我们。但我们确实找到了一篇广泛引用的文章,其中添加了一些背景信息......为了下一个人,我会在这里链接到它:http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

答案 8 :(得分:5)

如果我对此问题的体验给某人一个尤里卡!那一刻..我将添加以下内容:

当使用我使用IntelliJ生成的xsd文件时,我也遇到了这个问题&#34;从实例文档生成xsd&#34;菜单选项。

当我接受了此工具的所有默认值时,它生成了一个xsd文件,当与jaxb一起使用时,生成的java文件没有@XmlRootElement。在运行时,当我试图编组时,我遇到了与此问题中讨论的相同的异常。

我回到了IntellJ工具,并在&#34;设计类型&#34;中看到了默认选项。下降(当然我没有理解......但如果我是诚实的话,仍然不知道):

设计类型:

  

&#34;本地元素/全球复杂类型&#34;

我将此更改为

  

&#34;本地元素/类型&#34;

,现在它生成了一个(基本上)不同的xsd,当与jaxb一起使用时产生@XmlRootElement。不能说我理解它的内部和外部,但它对我有用。

答案 9 :(得分:4)

使用Maven构建,您可以添加@XmlRootElement注释

使用“jaxb2-basics-annotate”插件。

查看更多信息:见

Configure Maven to generate classes from XML Schema using JAXB

JAXB XJC code generation

答案 10 :(得分:3)

您是否尝试过改变这样的xsd?

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

答案 11 :(得分:3)

JAXBElement包装器适用于JAXB不生成@XmlRootElement的情况。这些包装器在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;
      }
 }

答案 12 :(得分:2)

经过两天的努力后,我找到了问题的解决方案。您可以使用 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();

答案 13 :(得分:1)

要解决它,你应该在使用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>

答案 14 :(得分:1)

该主题很老,但在企业业务环境中仍然有意义。我试图避免触摸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>

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

答案 15 :(得分:0)

我只是为同样的问题而苦苦挣扎了一段时间,只想发布我的最终结果对我来说很好。 因此,基本问题是:

  • 我必须从没有 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));
    }
}