Starting with XML Schema
In a previous answer, I described how to solve your use case, starting with Java objects. Based on your comments on this answer, this answer describes how the same can be done when a model is created from an XML schema.
XML Schema (attributeAdapter.xsd)
In this example, we will use the following XML schema:
<?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>
StringConverter
We will need to define a class to perform special String processing. For this use case, we want the null / property value to be treated as an empty String ("") in the XML document:
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; } }
Binding File (attributeAdapterBinding.xml)
We will need to use the JAXB binding file to configure the generation of the class. The binding file below will allow us to use the StringConverter class that we defined above:
<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 call
We will make our XJC call as follows:
xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
Domain Model (root)
The fields / properties that we configured in the binding file will be annotated using @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)
The generated XmlAdapter class will look something like this: Notice how it uses our StringConverter class:
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)); } }
Demo
Now, if we run the following demo code:
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); } }
Exit
We will get the desired result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root xmlns="http://www.example.com/adapter" foo="" bar=""/>
UPDATE (alternative bind file)
Alternatively, if you want the adapter to be applied to all properties like xsd:string , then you could use a binding file that looked something like this:
<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>