2012-01-10 1 views
8

Le but est de produire le code XML suivant avec JAXBJAXB @XmlValue générique

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

est-il une solution pour permettre @XmlValue génériques champs (je dois stocker byte[] et String données)? Voici ce que je désire:

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

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

Mais je reçois cette exception

(*) IllegalAnnotationException:
@ XmlAttribute/@ XMLValue besoin de faire référence à un type Java qui correspond à un texte en XML.

Répondre

8

Vous pouvez exploiter un XmlAdapter pour ce cas d'utilisation au lieu 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

Le XmlAdapter est associé à la propriété bars en utilisant l'annotation @XmlJavaTypeAdapter:

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; 
    } 
} 

Démo

Vous pouvez tester cet exemple en utilisant les éléments suivants morue démo 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); 
    } 

} 

Sortie

Notez comment la sortie inclut les attributs xsi:type pour préserver le type de la valeur. Vous pouvez éliminer l'attribut xsi:type en ayant votre XmlAdapter retour String au lieu de Object, si vous faites cela, vous aurez besoin gérer la conversion de String au type approprié vous pour l'opération 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

Y a-t-il une raison pour laquelle vous ne construisez pas simplement une chaîne avec votre byte []? Avez-vous vraiment besoin d'un générique?

+0

Cela nécessiterait que je convertisse des données binaires opaques en une chaîne, par exemple, je dois le coder manuellement, par exemple, hexBinary ou base64. Mais oui, c'est ce que j'utilise actuellement comme une solution de contournement. –

+0

Utilisez-vous votre propre algorithme de codage? Cela devrait être assez facile si vous utilisez [l'encodeur apache commons] (http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html). –

+0

J'utilise le [HexBinaryAdapter] (http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/HexBinaryAdapter.html) qui alt. Commons base64 classe, les deux qui sont gentils one-liners. –

4

Je ne pouvais pas obtenir @XmlValue en travaillant comme j'ai toujours eu NullPointerException en cours de route-je ne sais pas pourquoi. Je suis venu avec quelque chose comme le suivant à la place.

Déposez votre Bar classe tout à fait, parce que, comme vous voulez qu'il soit en mesure de contenir quoi que ce soit vous pouvez représenter simplement avec 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() {} 
} 

Sans dire JAXB qui NAMESPACES vos types utilisent tous les éléments bar dans un foo contiendrait des déclarations d'espace de noms séparés et des trucs-le package-info.java et tous les trucs de l'espace de noms sert uniquement fancification fins seulement.

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

L'exécution de ce test simple déclencherait quelque chose de similaire à votre extrait 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); 
} 

Sortie:

<?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 - Voici un exemple de la façon dont vous pourriez utiliser un 'XmlAdapter' pour faire la même chose, mais gardez la classe' Bar': http://stackoverflow.com/a/8901997/383861 –

+0

@BlaiseDoughan Merci pour le clarification! –

0

L'astuce je J'utilise généralement est de créer un schéma avec les types que vous voulez et ensuite utiliser xjc pour générer des classes Java et voir comment les annotations sont utilisées. :) Je crois en mappage de type approprié de schéma XML pour byte [] est 'base64Binary', créant ainsi le schéma comme celui-ci:

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

et en cours d'exécution xjc nous obtenir le code suivant généré:

@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)); 
} 
Questions connexes