2010-06-29 5 views
4

j'attendre le test suivant pour travailler avec JAXB RI 2.2.1.1 de Sun, mais il échoue avec un NullPointerException dans la construction du JAXBContext:champs de triage JAXB dont le type est un paramètre de type générique

public class GenericFieldMarshallTest { 

    public static class CustomType { 
    } 

    public static class CustomTypeAdapter extends XmlAdapter<String, CustomType> { 
     @Override 
     public String marshal(CustomType v) throws Exception { 
      return "CustomType"; 
     } 
     @Override 
     public CustomType unmarshal(String v) throws Exception { 
      return new CustomType(); 
     } 
    } 

    @XmlJavaTypeAdapter(type = CustomType.class, value = CustomTypeAdapter.class) 
    public static class RootElement<ValueType> { 
     @XmlValue public ValueType value; 
    } 

    @XmlRootElement(name = "root") 
    public static class CustomRootElement extends RootElement<CustomType> { 
     public CustomRootElement() { 
      value = new CustomType(); 
     } 
    } 

    @Test 
    public void test() throws Exception { 
     JAXBContext context = JAXBContext.newInstance(CustomRootElement.class, 
       CustomType.class, RootElement.class); 
     StringWriter w = new StringWriter(); 
     context.createMarshaller().marshal(new CustomRootElement(), w); 
     assertThat(w.toString(), equalTo("<root>CustomType</root>")); 
    } 

} 

L'exception je reçois est:

java.lang.NullPointerException 
     at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:165) 
     at com.sun.xml.bind.v2.runtime.property.ValueProperty.<init>(ValueProperty.java:77) 
     at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:106) 
     at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:179) 
     at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) 
     at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:166) 
     at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) 
     at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:330) 
     at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140) 
     at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154) 
     at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:597) 
     at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202) 
     at javax.xml.bind.ContextFinder.find(ContextFinder.java:363) 
     at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574) 
     at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522) 

la cause semble être que JAXB ne sait pas comment le maréchal type déclaré de mon champ (? que je pense est effacée à l'objet lors de l'exécution), même si lors de l'exécution, je ne jamais définissez le champ sur les types connus de JAXB. Comment puis-je marshaler un champ de type générique? (Le remplacement de @XmlValue par @XmlAttribute ne corrige pas l'exception, pas plus que le changement du type déclaré du champ en objet, bien que tout fonctionne bien si le champ est déclaré en tant que chaîne, sauf que la chaîne n'est pas assignable Le placement de @XmlJavaTypeAdapter ne fait aucune différence: dans mon code actuel, il est défini au niveau du package dans package-info.java.)

Répondre

2

Tout d'abord: votre XmlAdapter est incorrect. Les types génériques sont l'inverse. Ensuite, vous semblez avoir à mettre le @XmlJavaTypeAdapter sur CustomRootElement.

De plus, le JAXBContext doit être renseigné sur toutes les classes concernées. Créez un jaxb.index ou ObjectFactory et créez le contexte en attribuant le nom du package à la méthode newInstance ou répertoriez toutes les classes.

Le code complet (légèrement modifié comme j'utilise un main() et non une méthode de test JUnit):

public static class CustomType { 
} 

public static class CustomTypeAdapter extends 
     XmlAdapter<String, CustomType> { 

    @Override 
    public String marshal(CustomType v) throws Exception { 
     return "CustomType"; 
    } 

    @Override 
    public CustomType unmarshal(String v) throws Exception { 
     return new CustomType(); 
    } 

} 

public static class RootElement<V> { 
    public V value; 
} 

@XmlJavaTypeAdapter(CustomTypeAdapter.class) 
@XmlRootElement(name = "root") 
public static class CustomRootElement extends RootElement<CustomType> { 
    public CustomRootElement() { 
     value = new CustomType(); 
    } 
} 

public static void main(String[] args) throws Exception { 
    JAXBContext context = JAXBContext.newInstance(CustomRootElement.class, 
      CustomType.class, RootElement.class); 
    StringWriter w = new StringWriter(); 
    CustomRootElement cre = new CustomRootElement(); 
    cre.value = new CustomType(); 

    Marshaller marshaller = context.createMarshaller(); 
    marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE); 
    marshaller.marshal(cre, w); 

    System.err.println(w.toString()); 

    // just to see whether unmarshalling works too 
    CustomRootElement c = (CustomRootElement) context.createUnmarshaller() 
      .unmarshal(new StringReader(w.toString())); 
    System.err.println(c.value); 
} 

Maintenant, le résultat de marshalling un CustomRootElement n'est pas ce que vous attendez dans votre test (et ni est-il ce que j'attendais) mais vous pouvez le démasquer et obtenir ce que vous avez rassemblé précédemment. Donc, (un) travail, mais à la fois marshalling le XML ne semble pas aussi agréable:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<root> 
    <value xsi:type="customType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
</root> 

J'ai aussi mis un champ dans CustomType et cela a fonctionné aussi bien. Donc, si vous n'avez pas besoin du bon XML, cette solution devrait suffire. J'espère ne pas avoir oublié les changements que j'ai faits. Si je l'ai fait, laissez juste un commentaire et je modifierai en conséquence.

+0

Vous avez raison à propos de l'ordre des types de XmlAdapter, je voulais que ce soit l'inverse. C'était une erreur de transcription quand j'ai écrit le faux exemple pour ma question. J'ai corrigé ceci dans ma question. Toutefois, aucune de vos autres suggestions ne génère de code de travail pour moi. Le placement de l'annotation @XmlJavaTypeAdapter est arbitraire; dans mon code actuel, il est au niveau du paquet dans package-info.class. J'ai essayé d'ajouter les autres classes impliquées dans l'appel JAXBContext.newInstance() au cas où JAXB ne les trouve pas lui-même, mais cela ne fait pas non plus de différence avec le problème. – DanC

+0

Si vous avez une version de travail du test dans ma question, pouvez-vous la mettre dans un pastebin et la lier ici, afin que nous puissions voir ce que vous avez changé pour que cela fonctionne? – DanC

+0

J'ai édité le post. Vous n'avez pas besoin d'apporter des dépendances externes comme pastebin dans ceci. J'espère que cela fonctionne pour vous parce que j'ai utilisé JAXB comme il est distribué avec le JDK. – musiKk

Questions connexes