JAXB binding file: XmlAdapters and package name

南笙酒味 提交于 2019-12-20 10:33:08

问题


I have a binding file like this

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

The schema class are generated in "example" (correct), but the XmlAdapters in "org.w3._2001.xmlschema" (wrong). How can I fix this?


回答1:


I had this problem as well, solved it using this.

The basic premise is that you include a schema in your XJC compilation with following contents:

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

You then adjust the package name to where you want the generated adapters to be placed. XJC will believe that this schema is part of the schema set for W3C XML Schema itself and will honour the bindings in it.




回答2:


The org.w3._2001.xmlschema package is created here because XJC has to generate a class that extends javax.xml.bind.annotation.adapters.XmlAdapter, which in turn calls your parse/print static methods. For some reason, it puts them into this package, rather than somewhere more useful.

You haven't said which JAXB implementation you use, but the JAXB RI has an extension to the javaType binding customization which allows you to specify a subclass of XmlAdapter directly, rather than parseMethod/printMethod pairs. This removes the need to generate the synthetic XmlAdapter bridge class. See the RI docs for how to do this.

I imagine EclipseLink/Moxy has something similar to this, but I'm not sure if the XJC that ships with Java6 is capable of it (Sun seems to have removed half of the useful stuff from the RI when they brought it into the JRE).




回答3:


For Apache CXF users, the cleanest way is to use the -p option offered by wsdl2java.

-p [wsdl-namespace=]PackageName

Specifies zero, or more, package names to use for the generated code. Optionally specifies the WSDL namespace to package name mapping.

In our case

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

If you use the cxf-codegen-plugin, then just add another pair of <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>

No need for a targetNamespace pointing at the reserved XSD namespace and no need for catch-all jaxb package binding.




回答4:


The better way to use GlobalBinding is to specify explicit adapter instead of using this parse/print pair. For example, instead of the following:

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

Instead, you should:

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

Remember to add namespace for xjc:

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

The class LongAdapter would be like this:

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

}

In this way, since you specified adapter classes explicitely, jaxb won't generate default adapters with the default package name org.w3._2001.xmlschema.

It is very important to avoid to use the default package name org.w3._2001.xmlschema. Taking one example, if you have one project A and one project B, and both of them have some schema and bindings. In the old way, both of them generate adapters with exactly the same full qualified names, e.g. org.w3._2001.xmlschema.Adapter1. However, this adapter might be for Long in project A and for Integer in project B. Then, let's say you have a downstream project C using both A and B. Now the problem gets nasty. If C needs to use Adapter1, you cannot predict the used one is from A for Long or from B for Integer. Then, your application C might work fine in some time but maybe fail in a strange way in some other situations. If this happens, the type exception would be like:

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

The solution mentioned by Roy Truelove seems not working when I tried it in my environment with maven-jaxb2-plugin even if the theory is correct.




回答5:


Use the built in converters for common datatypes.

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


来源:https://stackoverflow.com/questions/5339615/jaxb-binding-file-xmladapters-and-package-name

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!