从 XML 架构开始
在previous answer 中,我描述了从 Java 对象开始时如何解决您的用例。根据您对该答案的 cmets,此答案描述了从 XML 模式生成模型时如何完成相同的事情。
XML 架构(attributeAdapter.xsd)
对于本示例,我们将使用以下 XML 模式:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema
elementFormDefault="qualified"
targetNamespace="http://www.example.com/adapter"
xmlns:nytd="http://www.example.com/adapter"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="foo" type="xs:string"/>
<xs:attribute name="bar" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
字符串转换器
我们需要定义一个类来进行特殊的字符串处理。对于这个用例,我们希望空字段/属性值在 XML 文档中被视为空字符串 (""):
package com.example.adapter;
public class StringConverter {
public static String parseString(String value) {
if("".equals(value)) {
return null;
}
return value;
}
public static String printString(String value) {
if(null == value) {
return "";
}
return value;
}
}
绑定文件(attributeAdapterBinding.xml)
我们将需要使用 JAXB 绑定文件来自定义类生成。下面的绑定文件将允许我们利用上面定义的 StringConverter 类:
<jaxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="attributeAdapter.xsd">
<jaxb:bindings node="//xs:element[@name='root']/xs:complexType">
<jaxb:bindings node="xs:attribute[@name='foo']">
<jaxb:property>
<jaxb:baseType>
<jaxb:javaType name="java.lang.String"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:baseType>
</jaxb:property>
</jaxb:bindings>
<jaxb:bindings node="xs:attribute[@name='bar']">
<jaxb:property>
<jaxb:baseType>
<jaxb:javaType name="java.lang.String"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:baseType>
</jaxb:property>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
XJC 通话
我们将按如下方式进行 XJC 调用:
xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
域模型(根)
我们在绑定文件中自定义的字段/属性会被@XmlJavaTypeAdapter注解;
package com.example.adapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "root")
public class Root {
@XmlAttribute
@XmlJavaTypeAdapter(Adapter1 .class)
protected String foo;
@XmlAttribute
@XmlJavaTypeAdapter(Adapter2 .class)
protected String bar;
public String getFoo() {
return foo;
}
public void setFoo(String value) {
this.foo = value;
}
public String getBar() {
return bar;
}
public void setBar(String value) {
this.bar = value;
}
}
XmlAdapter (Adapter1)
生成的 XmlAdapter 类将如下所示。注意它是如何利用我们的 StringConverter 类的:
package com.example.adapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class Adapter1 extends XmlAdapter<String, String> {
public String unmarshal(String value) {
return (com.example.adapter.StringConverter.parseString(value));
}
public String marshal(String value) {
return (com.example.adapter.StringConverter.printString(value));
}
}
演示
现在如果我们运行以下演示代码:
package com.example.adapter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.setFoo(null);
root.setBar(null);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
输出
我们会得到想要的输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://www.example.com/adapter" foo="" bar=""/>
更新(备用绑定文件)
或者,如果您希望适配器应用于xsd:string 类型的所有属性,那么您可以使用类似于以下内容的绑定文件;
<jaxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings>
<jaxb:javaType
name="String"
xmlType="xs:string"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:globalBindings>
</jaxb:bindings>