ejemplo - jaxb xjc download
Jaxb: xs: valores nulos de atributo (2)
A partir de objetos de Java
Para campos / propiedades mapeados como @XmlAttribute
, una implementación de JAXB (Metro, MOXy, JaxMe, etc.) calculará un valor String ("") vacío como property=""
. Puede usar un XmlAdapter
para exponer sus valores nulos como cadenas vacías para obtener el comportamiento deseado:
NullStringAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class NullStringAdapter extends XmlAdapter<String, String> {
@Override
public String unmarshal(String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
@Override
public String marshal(String v) throws Exception {
if(null == v) {
return "";
}
return v;
}
}
Raíz
A continuación se muestra cómo especifica el adaptador en su modelo de dominio. El mismo adaptador se puede usar en muchas propiedades:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Root {
private String foo;
private String bar;
@XmlAttribute
@XmlJavaTypeAdapter(NullStringAdapter.class)
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
@XmlAttribute
@XmlJavaTypeAdapter(NullStringAdapter.class)
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Manifestación
Puede demostrar el concepto ejecutando el siguiente código de demostración:
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
El siguiente es el resultado del código de demostración:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root bar="" foo=""/>
Para obtener más información sobre XmlAdapter de JAXB, consulte:
Reg: Jaxb
Básicamente estoy tratando de configurar un rol en JAXB que diga que siempre que se encuentre un campo nulo, en lugar de ignorarlo en la salida, configúrelo en un valor vacío.
Para xmlElement recibí una respuesta como si necesitáramos usar nillable = "true", pero para saber cómo debemos establecer el valor nulo. Al buscar en Google encontré que necesitamos usar use = "optional" pero no funciona en mi caso.
La parte de mi xsd está a continuación:
<xs:attribute name="RomVersion" type="xs:string" use="required" />
<xs:attribute name="MACAddress" type="xs:string" use="required" />
<xs:attribute name="LargestFreeBlock" type="xs:unsignedInt" use="required" />
<xs:attribute name="TimeSinceLastReset" type="xs:unsignedInt" use="optional" />
<xs:attribute name="ResetReason" type="xs:string" use="optional" />
<xs:attribute name="TimeStamp" type="xs:unsignedInt" use="optional" />
<xs:attribute name="ECOList" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
Por favor, dame la solución lo antes posible si alguien sabe.
A partir del esquema XML
En una respuesta anterior describí cómo resolver tu caso de uso cuando comenzaste desde 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 usaremos 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
Tendremos que definir una clase para hacer nuestro manejo de cadenas especial. Para este caso de uso, queremos que un valor de campo / propiedad nulo se trate 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;
}
}
Archivo de enlace (attributeAdapterBinding.xml)
Tendremos que 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>
Llamada XJC
Haremos nuestra llamada XJC de la siguiente manera:
xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
Modelo de dominio (raíz)
Los campos / propiedades que hemos personalizado 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 se verá algo como lo siguiente. Observe cómo 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));
}
}
Manifestación
Ahora si ejecutamos 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 (archivo de enlace alternativo)
Alternativamente, si quiere que el adaptador se aplique a todas las propiedades de tipo xsd:string
entonces podría usar un archivo de enlace que se parezca a algo similar;
<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>