2012-08-15 12 views
9

Con suerte una tarea fácil para los expertos JAXB:XmlJavaTypeAdapter no su detección

Estoy tratando de formar una clase inmutable que hace no definir un constructor sin argumentos por defecto. Definí una implementación XmlAdapter pero parece que no se recogió. He creado un ejemplo simple y autónomo, que todavía no funciona. ¿Alguien puede aconsejarme qué estoy haciendo mal?

Clase Inmutable

@XmlJavaTypeAdapter(FooAdapter.class) 
@XmlRootElement 
public class Foo { 
    private final String name; 
    private final int age; 

    public Foo(String name, int age) { 
    this.name = name; 
    this.age = age; 
    } 

    public String getName() { return name; } 
    public int getAge() { return age; } 
} 

adaptador y valor Tipo

public class FooAdapter extends XmlAdapter<AdaptedFoo, Foo> { 
    public Foo unmarshal(AdaptedFoo af) throws Exception { 
    return new Foo(af.getName(), af.getAge()); 
    } 

    public AdaptedFoo marshal(Foo foo) throws Exception { 
    return new AdaptedFoo(foo); 
    } 
} 

class AdaptedFoo { 
    private String name; 
    private int age; 

    public AdaptedFoo() {} 

    public AdaptedFoo(Foo foo) { 
    this.name = foo.getName(); 
    this.age = foo.getAge(); 
    } 

    @XmlAttribute 
    public String getName() { return name; } 
    public void setName(String name) { this.name = name; } 

    @XmlAttribute 
    public int getAge() { return age; } 
    public void setAge(int age) { this.age = age; } 
} 

Marshaller

public class Marshal { 
    public static void main(String[] args) { 
    Foo foo = new Foo("Adam", 34); 

    try { 
     JAXBContext jaxbContext = JAXBContext.newInstance(Foo.class); 
     Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 

     // output pretty printed 
     jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     jaxbMarshaller.marshal(foo, System.out);    
    } catch (JAXBException e) { 
     e.printStackTrace(); 
    } 
    } 
} 

Seguimiento de la pila

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions 
Foo does not have a no-arg default constructor. 
     this problem is related to the following location: 
       at Foo 

     at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91) 
     at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:451) 
     at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:283) 
     at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:126) 
     at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1142) 
     at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:130) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
     at java.lang.reflect.Method.invoke(Method.java:601) 
     at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248) 
     at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235) 
     at javax.xml.bind.ContextFinder.find(ContextFinder.java:445) 
     at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637) 
     at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584) 
     at Marshal2.main(Marshal2.java:11) 

Tenga en cuenta que estoy usando JDK 1.7.0_05.

Respuesta

7

Lo siguiente debe ayudar a:

FOO AS objeto raíz

Cuando @XmlJavaTypeAdapter se especifica en el nivel de tipo sólo se aplica a los campos/propiedades referencia a esa clase, y no cuando una instancia de esa clase es un objeto raíz en su árbol XML. Esto significa que tendrá que convertir Foo a AdaptedFoo usted mismo, y crear el JAXBContext en AdaptedFoo y no Foo.

Mariscal

package forum11966714; 

import javax.xml.bind.*; 

public class Marshal { 
    public static void main(String[] args) { 
     Foo foo = new Foo("Adam", 34); 

     try { 
     JAXBContext jaxbContext = JAXBContext.newInstance(AdaptedFoo.class); 
     Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 

     // output pretty printed 
     jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     jaxbMarshaller.marshal(new AdaptedFoo(foo), System.out);    
     } catch (JAXBException e) { 
     e.printStackTrace(); 
     } 
    } 
    } 

AdaptedFoo

Usted tendrá que añadir una anotación @XmlRootElement a la clase AdaptedFoo. Puede eliminar la misma anotación de la clase Foo.

package forum11966714; 

import javax.xml.bind.annotation.*; 

@XmlRootElement 
class AdaptedFoo { 
    private String name; 
    private int age; 

    public AdaptedFoo() { 
    } 

    public AdaptedFoo(Foo foo) { 
     this.name = foo.getName(); 
     this.age = foo.getAge(); 
    } 

    @XmlAttribute 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @XmlAttribute 
    public int getAge() { 
     return age; 
    } 

    public void setAge(int age) { 
     this.age = age; 
    } 
} 

FOO AS objeto anidado

Cuando Foo no es el objeto de raíz todo funciona de la manera que lo tienes asignada. He ampliado su modelo para demostrar cómo esto funcionaría.

Bar

package forum11966714; 

import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class Bar { 

    private Foo foo; 

    public Foo getFoo() { 
     return foo; 
    } 

    public void setFoo(Foo foo) { 
     this.foo = foo; 
    } 

} 

demostración

Tenga en cuenta que la implementación de referencia JAXB no le permitirá especificar la clase Foo cuando el bootstrapping JAXBContext.

package forum11966714; 

import java.io.File; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 

public class Demo { 
    public static void main(String[] args) { 
     try { 
      JAXBContext jaxbContext = JAXBContext.newInstance(Bar.class); 

      Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); 
      File xml = new File("src/forum11966714/input.xml"); 
      Bar bar = (Bar) jaxbUnmarshaller.unmarshal(xml); 

      Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 
      jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
      jaxbMarshaller.marshal(bar, System.out); 
     } catch (JAXBException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

input.xml/salida

<?xml version="1.0" encoding="UTF-8"?> 
<bar> 
    <foo name="Jane Doe" age="35"/> 
</bar> 
+1

Gracias Blaise! (Creo que fue tu blog el que originalmente estaba viendo). Parece que el problema fue que especifiqué Foo.class al JAXBContext durante el arranque. La vergüenza de JAXB no funciona en este punto que he especificado un adaptador para Foo ... hace que el arranque sea un poco complicado. – Adamski