2011-02-03 7 views
5

Estoy cambiando de Castor a JAXB2 para realizar el marshaling/unmarshaling entre objetos XML y Java. Tengo problemas para configurar una colección de objetos polimórficos.Configuración de la colección de objetos polimórficos para trabajar en JAXB2

XML de ejemplo

<project name="test project"> 
    <orange name="fruit orange" orangeKey="100" /> 
    <apple name="fruit apple" appleKey="200" /> 
    <orange name="fruit orange again" orangeKey="500" /> 
</project> 

clase Proyecto

La lista oranges funciona bien, estoy viendo 2 naranjas en la lista. Pero no estoy seguro de cómo configurar fruitList. El fruitList debe tener 3 frutas: 2 naranjas y 1 manzana.

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Project { 

    @XmlAttribute 
    private String   name; 

    @XmlElement(name = "orange") 
    private List<Orange> oranges  = new ArrayList<Orange>(); 

    // Not sure how to configure this... help! 
    private List<Fruit>  fruitList = new ArrayList<Fruit>(); 
} 

clase de frutas

La fruta es una clase abstracta. Por alguna razón, definir esta clase como un resumen parece estar causando muchos problemas.

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public abstract class Fruit { 

    @XmlAttribute 
    private String name; 
} 

clase de Orange clase

public class Orange extends Fruit { 

    @XmlAttribute 
    private String orangeKey; 
} 

de Apple

public class Apple extends Fruit { 

    @XmlAttribute 
    private String appleKey; 
} 

¿Cómo puedo configurar mi fruitList en Project para lograr lo que quiero aquí?

¡Muchas gracias!

Respuesta

4

Usted desea aprovechar @XmlElementRef esto es se corresponde con el concepto de esquema XML de grupos de sustitución que corresponde a su pregunta.

Paso 1 - Usando @XmlElementRef

La propiedad fruitList se anota con @XmlElementRef:

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

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlElementRef; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Project { 

    @XmlAttribute 
    private String name; 

    @XmlElementRef 
    private List<Fruit> fruitList = new ArrayList<Fruit>(); 

} 

Paso 2 - Anotar Apple y naranja con @XmlRootElement

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Apple extends Fruit { 

    @XmlAttribute 
    private String appleKey; 

} 

y

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Orange extends Fruit { 

    @XmlAttribute 
    private String orangeKey; 

} 

Código demostración

El código siguiente se puede utilizar para demostrar la solución:

import java.io.File; 

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

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Project.class, Apple.class, Orange.class); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Project project = (Project) unmarshaller.unmarshal(new File("input.xml")); 

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

} 

Para más información:

+0

Estoy probando su enfoque ... Anoté el 'fruitList' propiedad con '@ XmlElementRef' y anotaciones' Orange' y 'Apple' classes con' @ XmlRootElement'. Ejecuté el código, y el 'fruitList' salió vacío. ¿Qué estoy haciendo mal aquí? Gracias. – limc

+1

Descubrí el problema, tengo que agregar 'Orange.class' y' Apple.class' en 'JAXBContext.newInstance (..)' junto con 'Project'. Funciona ahora Gracias. – limc

0

Put XmlAnyElement anotación:

@XmlAnyElement(lax = true) 
private List<Fruit>  fruitList = new ArrayList<Fruit>(); 
+0

que no funcionó ... 'fruitList' contiene un' 'tipo de apple' ElementNSImpl'. – limc

+0

Para @XmlAnyElement, necesitaría agregar @XmlRootElement a cada una de las subclases. Sin embargo, esto es más para las secciones comodín. Como los objetos secundarios están en una relación de herencia, @XmlElementRef es una mejor opción aquí. Para obtener más información sobre @XmlAnyElement, consulte: http: //bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html –

2

Después de pelearse alrededor ... creo que tengo trabajo ahora: -

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Project { 

    @XmlAttribute 
    private String  name; 

    // this has to be commented out, or else oranges won't show up in fruitList 
    // @XmlElement(name = "orange") 
    // private List<Orange> oranges = new ArrayList<Orange>(); 

    @XmlElements({ 
      @XmlElement(name = "orange", type = Orange.class), 
      @XmlElement(name = "apple", type = Apple.class) 
    }) 
    private List<Fruit> fruitList = new ArrayList<Fruit>(); 

} 
+1

@XmlElements funcionará, pero cada vez que agregue una nueva subclase tendrá que editar la clase Project. Si usa @XmlElementRef en su lugar, solo tendrá que anotar la nueva subclase con @XmlRootElement. @XmlElements es más para representar estructuras de elección: http://bdoughan.blogspot.com/2010/10/jaxb-and-xsd-choice-xmlelements.html –

Cuestiones relacionadas