JAXB绑定文件:XmlAdapters和包名

时间:2011-03-17 13:24:35

标签: java jaxb xjc

我有像这样的绑定文件

<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <jxb:bindings schemaLocation="example.xsd" node="/xs:schema">
    <jxb:schemaBindings>
        <jxb:package name="example" />
    </jxb:schemaBindings>
    <jxb:globalBindings>
        <jxb:javaType name="java.util.Calendar" xmlType="xs:dateTime"
            parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime"
            printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
        <jxb:javaType name="java.util.Calendar" xmlType="xs:date"
            parseMethod="javax.xml.bind.DatatypeConverter.parseDate"
            printMethod="javax.xml.bind.DatatypeConverter.printDate" />
        <jxb:javaType name="java.util.Calendar" xmlType="xs:time"
            parseMethod="javax.xml.bind.DatatypeConverter.parseTime"
            printMethod="javax.xml.bind.DatatypeConverter.printTime" />
    </jxb:globalBindings>

  </jxb:bindings>
</jxb:bindings>

模式类在“example”(正确)中生成,但是“org.w3._2001.xmlschema”中的XmlAdapters(错误)。我该如何解决这个问题?

5 个答案:

答案 0 :(得分:11)

此处创建了org.w3._2001.xmlschema包,因为XJC必须生成一个扩展javax.xml.bind.annotation.adapters.XmlAdapter的类,后者又会调用您的解析/打印静态方法。出于某种原因,它将它们放入这个包中,而不是更有用的地方。

您还没有说过您使用的JAXB实现,但JAXB RI对javaType绑定自定义有一个扩展,它允许您直接指定XmlAdapter的子类,而不是{{1 } / parseMethod对。这消除了生成合成printMethod桥类的需要。有关如何执行此操作,请参阅RI docs

我想EclipseLink / Moxy有类似的东西,但是我不确定Java6附带的XJC是否能够实现它(当它们带来它时,Sun似乎从RI中删除了一半有用的东西JRE)。

答案 1 :(得分:9)

对于Apache CXF用户,最简洁的方法是使用-p提供的wsdl2java选项。

  

-p [wsdl-namespace =] PackageName

     

指定要用于生成的代码的零个或多个包名称。   (可选)指定包名称映射的WSDL名称空间。

在我们的案例中

  

-p http://www.w3.org/2001/XMLSchema=org.acme.foo

如果您使用cxf-codegen-plugin,则只需添加另一对<extraarg>

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>${cxf.version}</version>
        [...]
    <extraarg>-p</extraarg>
    <extraarg>http://www.w3.org/2001/XMLSchema=org.acme.foo</extraarg>
        [...]
</plugin>

不需要指向保留的XSD名称空间的targetNamespace,也不需要catch-all jaxb包绑定。

答案 2 :(得分:7)

我也遇到了这个问题,使用this解决了这个问题。

基本前提是您在XJC编译中包含一个模式,其中包含以下内容:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://www.w3.org/2001/XMLSchema"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  jaxb:version="2.0">
  <annotation><appinfo>
    <jaxb:schemaBindings>
      <jaxb:package name="org.acme.foo"/>
    </jaxb:schemaBindings>
  </appinfo></annotation>
</schema>

然后,将程序包名称调整为要放置生成的适配器的位置。 XJC将相信此模式是W3C XML Schema本身的模式集的一部分,并将遵守其中的绑定。

答案 3 :(得分:5)

使用GlobalBinding的更好方法是指定显式适配器,而不是使用此解析/打印对。例如,而不是以下内容:

<jaxb:javaType name="java.lang.Long" xmlType="xs:long"
                      parseMethod="com.mypackage.myclass.parseLong"
                  printMethod="com.mypackage.myclass.print"/>

相反,你应该:

<xjc:javaType name="java.lang.Long" xmlType="xs:long"
                  adapter="com.mypackage.LongAdapter"/>

请记住为xjc添加名称空间:

xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
          jxb:extensionBindingPrefixes="xjc"

LongAdapter类是这样的:

public class LongAdapter
extends XmlAdapter<String, Long>
{


public Long unmarshal(String value) {
    return your_util_class.parseLong(value);
}

public String marshal(Long value) {
    return your_util_class.print(value);
}

}

通过这种方式,由于您明确指定了适配器类,因此jaxb不会使用默认包名org.w3._2001.xmlschema生成默认适配器。

避免使用默认包名org.w3._2001.xmlschema非常重要。举一个例子,如果你有一个项目A和一个项目B,并且它们都有一些模式和绑定。以旧的方式,它们都生成具有完全相同的完全限定名称的适配器,例如, org.w3._2001.xmlschema.Adapter1。但是,这个适配器可能是项目A中的Long和项目B中的Integer。然后,让我们说你有一个使用A和B的下游项目C.现在问题变得很糟糕。如果C需要使用Adapter1,则无法预测使用的是来自A for Long或B来自Integer。然后,您的应用程序C可能会在一段时间内正常工作,但在某些其他情况下可能会以奇怪的方式失败。如果发生这种情况,类型例外将是:

org.w3._2001.xmlschema.Adapter1 is not applicable to the field type java.lang.Double...

当我使用maven-jaxb2-plugin在我的环境中尝试它时,Roy Truelove提到的解决方案似乎无效,即使理论是正确的。

答案 4 :(得分:0)

将内置的转换器用于常见的数据类型。

<jxb:javaType name="java.lang.Integer" xmlType="xs:integer"              
parseMethod="javax.xml.bind.DatatypeConverter.parseInt"                  
printMethod="javax.xml.bind.DatatypeConverter.printInt" />