2012-01-10 36 views
8

El objetivo es producir el siguiente código XML con JAXBJAXB @XmlValue genérica

<foo> 
    <bar>string data</bar> 
    <bar>binary data</bar> 
</foo> 

¿Hay una solución para permitir genéricos@XmlValue campos (que necesito para almacenar y byte[]String datos)? A continuación es lo que deseo:

@XmlRootElement 
public class Foo { 
    private @XmlElement List<Bar> bars; 
} 

@XmlRootElement 
public class Bar<T> { 
    private @XmlValue T value; // (*) 
} 

Pero consigo esta excepción

(*) IllegalAnnotationException:
@ XmlAttribute/@ XmlValue que hacer referencia a un tipo Java que se asigna al texto en XML.

Respuesta

8

Se podría aprovechar un XmlAdapter para este caso de uso en lugar de @XmlValue:

BarAdapter

package forum8807296; 

import javax.xml.bind.annotation.adapters.XmlAdapter; 

public class BarAdapter extends XmlAdapter<Object, Bar<?>> { 

    @Override 
    public Bar<?> unmarshal(Object v) throws Exception { 
     if(null == v) { 
      return null; 
     } 
     Bar<Object> bar = new Bar<Object>(); 
     bar.setValue(v); 
     return bar; 
    } 

    @Override 
    public Object marshal(Bar<?> v) throws Exception { 
     if(null == v) { 
      return null; 
     } 
     return v.getValue(); 
    } 

} 

Foo

El XmlAdapter está asociado con la propiedad bars utilizando el @XmlJavaTypeAdapter anotación:

package forum8807296; 

import java.util.List; 
import javax.xml.bind.annotation.*; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 

@XmlRootElement 
public class Foo { 
    private List<Bar> bars; 

    @XmlElement(name="bar") 
    @XmlJavaTypeAdapter(BarAdapter.class) 
    public List<Bar> getBars() { 
     return bars; 
    } 

    public void setBars(List<Bar> bars) { 
     this.bars = bars; 
    } 

} 

Bar

package forum8807296; 

public class Bar<T> { 
    private T value; 

    public T getValue() { 
     return value; 
    } 

    public void setValue(T value) { 
     this.value = value; 
    } 
} 

demostración

Puede probar este ejemplo utilizando la siguiente demo bacalao e:

package forum8807296; 

import java.util.ArrayList; 
import java.util.List; 

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(Foo.class); 

     Foo foo = new Foo(); 
     List<Bar> bars = new ArrayList<Bar>(); 
     foo.setBars(bars); 

     Bar<String> stringBar = new Bar<String>(); 
     stringBar.setValue("string data"); 
     bars.add(stringBar); 

     Bar<byte[]> binaryBar = new Bar<byte[]>(); 
     binaryBar.setValue("binary data".getBytes()); 
     bars.add(binaryBar); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(foo, System.out); 
    } 

} 

salida

Nota cómo el resultado incluye los atributos xsi:type para preservar el tipo del valor. Puede eliminar el atributo xsi:type por tener su XmlAdapter retorno String en lugar de Object, si lo hace tendrá que manejar la conversión de String al tipo apropiado a sí mismo para la operación unmarshal:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<foo> 
    <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">string data</bars> 
    <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:base64Binary">YmluYXJ5IGRhdGE=</bars> 
</foo> 
0

¿Hay alguna razón por la que simplemente no construyes un String con tu byte []? ¿Realmente necesitas un genérico?

+0

Eso requeriría convertir datos binarios opacos a una Cadena, por ejemplo, debo codificarla manualmente en, por ejemplo, hexBinary o base64. Pero sí, eso es lo que estoy usando actualmente como solución alternativa. –

+0

¿Está utilizando su propio algoritmo de codificación? Debería ser bastante sencillo si usa el [codificador de Apache commons] (http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html). –

+0

Estoy usando el [HexBinaryAdapter] (http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/HexBinaryAdapter.html) que alt. clase commons base64, ambas son lindas con una línea. –

4

No pude obtener @XmlValue trabajando como siempre recibí NullPointerException en el camino, no estoy seguro de por qué. En su lugar, se me ocurrió algo como lo siguiente.

Caída de su clase Bar por completo, porque, como usted quiere que sea capaz de contener nada simplemente puede representar con Object.

@XmlRootElement(name = "foo", namespace = "http://test.com") 
@XmlType(name = "Foo", namespace = "http://test.com") 
public class Foo { 

    @XmlElement(name = "bar") 
    public List<Object> bars = new ArrayList<>(); 

    public Foo() {} 
} 

Sin decirle JAXB el que los tipos de espacios de nombres están utilizando cada elemento dentro de un barfoo contendría declaraciones de espacios de nombres distintos y cosas por el package-info.java y todas las cosas de espacio de nombres sólo sirve sólo fancification propósitos.

@XmlSchema(attributeFormDefault = XmlNsForm.QUALIFIED, 
      elementFormDefault = XmlNsForm.QUALIFIED, 
      namespace = "http://test.com", 
      xmlns = { 
       @XmlNs(namespaceURI = "http://test.com", prefix = ""), 
       @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"), 
       @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema", prefix = "xs")}) 
package test; 

import javax.xml.bind.annotation.XmlNs; 
import javax.xml.bind.annotation.XmlNsForm; 
import javax.xml.bind.annotation.XmlSchema; 

La ejecución de esta sencilla prueba arrojaría algo similar a su fragmento de XML.

public static void main(String[] args) throws JAXBException { 
    JAXBContext context = JAXBContext.newInstance(Foo.class); 

    Foo foo = new Foo(); 
    foo.bars.add("a"); 
    foo.bars.add("b".getBytes()); 

    Marshaller marshaller = context.createMarshaller(); 
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
    marshaller.marshal(foo, System.out); 
} 

Salida:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<foo xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <bar xsi:type="xs:string">a</bar> 
    <bar xsi:type="xs:base64Binary">Yg==</bar> 
</foo> 
+1

+1 - Aquí hay un ejemplo de cómo se puede usar un 'XmlAdapter' para hacer lo mismo, pero mantener la clase' Bar': http://stackoverflow.com/a/8901997/383861 –

+0

@BlaiseDoughan Gracias por la ¡aclaración! –

0

El truco I' m usualmente es crear un esquema con los tipos que deseas y luego usar xjc para generar clases Java y ver cómo se usan las anotaciones. :) Creo en la asignación de tipo adecuado esquema XML para byte [] es 'base64Binary', por lo que la creación de esquema de la siguiente manera:

<?xml version="1.0" encoding="UTF-8"?> 
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified"> 
    <element name="aTest" type="base64Binary"></element> 
</schema> 

y funcionando xjc obtendríamos siguiente código generado:

@XmlElementDecl(namespace = "http://www.example.org/NewXMLSchema", name = "aTest") 
public JAXBElement<byte[]> createATest(byte[] value) { 
    return new JAXBElement<byte[]>(_ATest_QNAME, byte[].class, null, ((byte[]) value)); 
}