2011-05-13 13 views
6

reg: jaxbjaxb: xs: valores nulos de atributos

básicamente estoy tratando de establecer un papel en JAXB que dice que cada vez que se encuentra un campo nulo, en lugar de ignorarlo en la salida, lo puso a un valor vacío

Para xmlElement Recibí la respuesta como si necesitáramos usar nillable = "true" pero para la forma en que necesitamos establecer el valor nulo. Al buscar en Google encontré que necesitamos usar use = "optional" pero no funciona en mi caso. parte

Mi de xsd es el siguiente:

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

Respuesta

5

A partir de objetos Java

Para campos/propiedades asignadas como @XmlAttribute, una aplicación JAXB (Metro, moxy, JaxMe, etc) ordenarán una cadena vacía valor ("") como property="". Se puede utilizar un XmlAdapter para exponer sus valores nulos como cadenas vacías para conseguir 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

La siguiente es la forma de especificar el adaptador en su modelo de dominio. El mismo adaptador se puede utilizar 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; 
    } 

} 

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

La siguiente es la salida de código de demostración:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<root bar="" foo=""/> 

Para más información sobre XmlAdapter de JAXB Ver:

+0

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

+0

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

+0

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

6

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

Funciona bien. Utilicé GlobalBindings porque mis xsd son muy grandes, por lo que la creación de binding.xml será un proceso agitado. pero si quiero aplicar lo mismo por mucho tiempo, int etc. no solo cadena, ¿puedo definirlo en uniones globales de la misma manera que una cadena? – Rekha

+1

@Rekha - Debería poder usar la misma estrategia para cualquier tipo simple. –