我试图从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,然后包括所有类型。


当前回答

这个主题相当古老,但在企业业务上下文中仍然相关。我尽量避免接触xsds,以便将来可以轻松地更新它们。以下是我的解决方案。

1. 通常xjc:简单就足够了

<?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>

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

其他回答

万一我对这个问题的经验给了某人一个顿悟!时刻. .我补充如下:

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

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

我回到IntellJ工具,在“设计类型”下拉菜单中看到了默认选项(当然我不明白…老实说,我现在也不知道)是:

设计类型:

“局部元素/全局复杂类型”

我把这个改为

“local文本/ types”

,现在它生成了一个(实质上)不同的xsd,当与jaxb一起使用时,生成了@XmlRootElement。我不能说我理解它的来龙去脉,但它对我很管用。

这个主题相当古老,但在企业业务上下文中仍然相关。我尽量避免接触xsds,以便将来可以轻松地更新它们。以下是我的解决方案。

1. 通常xjc:简单就足够了

<?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>

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

你试过这样改变你的xsd吗?

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

Joe's answer (Joe Jun 26 '09 at 17:26) does it for me. The simple answer is that absence of an @XmlRootElement annotation is no problem if you marshal a JAXBElement. The thing that confused me is the generated ObjectFactory has 2 createMyRootElement methods - the first takes no parameters and gives the unwrapped object, the second takes the unwrapped object and returns it wrapped in a JAXBElement, and marshalling that JAXBElement works fine. Here's the basic code I used (I'm new to this, so apologies if the code's not formatted correctly in this reply), largely cribbed from 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;
  }
}

@XmlRootElement不需要解组-如果使用2参数形式的Unmarshaller#unmarshall。

所以,如果不这样做:

UserType user = (UserType) unmarshaller.unmarshal(new StringReader(responseString));

一个人应该做:

JAXBElement<UserType> userElement = unmarshaller.unmarshal(someSource, UserType.class);
UserType user = userElement.getValue();

后一种代码将不需要UserType类级别的@XmlRootElement注释。