A partir de esquemas XML
En un previous answer he descrito cómo resolver su caso de uso cuando a partir de objetos Java.Según sus comentarios a esa respuesta, esta respuesta describe cómo se puede hacer lo mismo cuando el modelo se genera a partir de un esquema XML.
esquema XML (attributeAdapter.xsd)
Para este ejemplo utilizaremos el siguiente esquema 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>
StringConverter
Necesitaremos define una clase para hacer nuestro manejo de cadenas especial. Para este caso de uso que queremos un valor de campo/propiedad nula a ser tratada como una cadena vacía ("") en el documento 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;
}
}
encuadernación de archivos (attributeAdapterBinding.xml)
Nosotros necesitará usar un archivo de enlace JAXB para personalizar la generación de clases. El archivo de enlace a continuación nos permitirá aprovechar la clase StringConverter que definimos anteriormente:
<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 llamar
Haremos nuestro llamado XJC de la siguiente manera:
xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
Modelo de dominio (raíz)
Los campos/propiedades que personalizamos en el archivo de enlace se anotarán con @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)
La clase XmlAdapter generada será algo como lo siguiente.Tenga en cuenta cómo se aprovecha nuestra clase 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));
}
}
demostración
Ahora bien, si corremos el siguiente código de demostración:
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);
}
}
salida
obtendremos el resultado deseado:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://www.example.com/adapter" foo="" bar=""/>
ACTUALIZACIÓN (Alternate archivo de enlace)
Alternativamente, si desea el adaptador se aplica a todas las propiedades de tipo xsd:string
entonces se podría utilizar un archivo de enlace que parecía algo como;
<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>
Pero los archivos de clase se generan utilizando el comando xjc. No se supone que hagamos ningún cambio en eso ... Me refiero al cambio: @XmlJavaTypeAdapter (NullStringAdapter.class) – Rekha
@Rekha - He agregado una segunda respuesta que describe cómo manejar este caso de uso cuando se comienza desde un esquema XML: http://stackoverflow.com/questions/5989922/jaxb-xsattribute-null-values/6018318#6018318 –
Ahora el cliente quiere que aparezcan valores predeterminados en lugar de cadenas en blanco. Me gusta: ... Entonces asumir este fragmento XML: < ... Primero = "31" Tercero = "42" ... /> El xml deseado debería ser como: <... Primero = "31" Segundo = "2" Tercero = "42" ... /> –
Rekha