2011-03-16 23 views
7

Tengo la siguiente estructura XML, que está modelando un único concepto en múltiples elementos XML. Este formato no está bajo mi control.JAXB desmaterialización de múltiples elementos XML en una sola clase

<Output> 
    <Wrapper> 
    <Channel> 
     <id>1</id> 
     <type>x</type> 
    </Channel> 
    <Channel> 
     <id>2</id> 
     <type>y</type> 
    </Channel> 
    <ChannelName> 
     <id>1</id> 
     <name>Channel name</name> 
    </ChannelName> 
    <ChannelName> 
     <id>2</id> 
     <name>Another channel name</name> 
    </ChannelName> 
    </Wrapper> 
</Output> 

Quiero modelar esto en una base de datos que tiene control sobre y puede tener un más sencilla tabla de Channel con id, type y name campos. Por lo tanto, me gustaría separarme en un solo List<Channel> en la clase Wrapper.

¿Se puede hacer esto con @Xml... anotaciones automáticamente? Actualmente estoy usando JAXB para unificar en listas de clases @XmlElement(name="Channel") y @XmlElement(name="ChannelName") y luego procesar el transitorio ChannelName/name en el Channel, pero estoy pensando que debe haber una forma automatizada más fácil de asignar estos elementos. ¿O es un trabajo para XSLT?

Puede ser útil saber que el archivo XML está entrando como un archivo POST de HTTP y estoy usando Spring 3, Java e Hibernate. Estoy esperando algo en EclipseLink JAXB (MOXy) podría ayudar :)

Respuesta

12

@XmlElementWrapper hará el trabajo:

@XmlElementWrapper(name="Wrapper") 
@XmlElement(name="Channel") 
private List<Channel> channels; 

Para los casos más avanzados se puede utilizar la extensión @XmlPath en EclipseLink JAXB (moxy):


Esto es lo que tengo hasta ahora. Todavía estoy tratando de eliminar la necesidad de los objetos auxiliares. Este ejemplo requiere EclipseLink JAXB (MOXy).

objetos del modelo

Sus objetos del modelo son:

package example; 

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

public class Wrapper { 

    private List<Channel> channels = new ArrayList<Channel>(); 

    public List<Channel> getChannels() { 
     return channels; 
    } 

    public void setChannels(List<Channel> channels) { 
     this.channels = channels; 
    } 

} 

y:

package example; 

import javax.xml.bind.annotation.XmlID; 

public class Channel { 

    private String id; 
    private String type; 
    private String name; 

    @XmlID 
    public String getId() { 
     return id; 
    } 

    public void setId(String id) { 
     this.id = id; 
    } 

    public String getType() { 
     return type; 
    } 

    public void setType(String type) { 
     this.type = type; 
    } 

    public String getName() { 
     return name; 
    } 

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

} 

Helper Objects

Mi solución actual implica s algunos objetos de ayuda:

package example.adapted; 

import java.util.List; 
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlElementWrapper; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.XmlTransient; 
import javax.xml.bind.annotation.XmlType; 

import example.Channel; 
import example.Wrapper; 

@XmlRootElement(name="Output") 
@XmlType(propOrder={"channels", "channelNames"}) 
public class AdaptedWrapper { 

    private Wrapper wrapper = new Wrapper(); 
    private List<ChannelName> channelNames; 

    @XmlTransient 
    public Wrapper getWrapper() { 
     for(ChannelName channelName : channelNames) { 
      channelName.getChannel().setName(channelName.getName()); 
     } 
     return wrapper; 
    } 

    @XmlElementWrapper(name="Wrapper") 
    @XmlElement(name="Channel") 
    public List<Channel> getChannels() { 
     return wrapper.getChannels(); 
    } 

    public void setChannels(List<Channel> channels) { 
     wrapper.setChannels(channels); 
    } 

    @XmlElementWrapper(name="Wrapper") 
    @XmlElement(name="ChannelName") 
    public List<ChannelName> getChannelNames() { 
     return channelNames; 
    } 

    public void setChannelNames(List<ChannelName> channelNames) { 
     this.channelNames = channelNames; 
    } 

} 

y:

package example.adapted; 

import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlIDREF; 

import example.Channel; 

public class ChannelName { 

    private String name; 
    private Channel channel; 

    public String getName() { 
     return name; 
    } 

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

    @XmlIDREF 
    @XmlElement(name="id") 
    public Channel getChannel() { 
     return channel; 
    } 

    public void setChannel(Channel channel) { 
     this.channel = channel; 
    } 

} 

Código demostración

package example; 

import java.io.File; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 

import example.adapted.AdaptedWrapper; 

public class Demo { 

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

     File xml = new File("input.xml"); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     AdaptedWrapper adaptedWrapper = (AdaptedWrapper) unmarshaller.unmarshal(xml); 
     Wrapper wrapper = adaptedWrapper.getWrapper(); 

     for(Channel channel : wrapper.getChannels()) { 
      System.out.println(channel.getName()); 
     } 
    } 

} 
+0

Esperaba que estuvieras en @Blaise :) Eché un vistazo a XmlPath pero no pude encontrar la forma de buscar un nombre para un ID determinado. Así que cuando desempaque el primer 'Channel', el XPath que quiero recoger es' ChannelName [id = "1"]/name' pero no pude ver cómo conectar el 'id'. Tendré un mira tu blog ahora mismo – andyb

+0

@andyb: Actualmente estamos trabajando en una función relacionada (consulte https://bugs.eclipse.org/339596). La fase inicial está dirigida a las condiciones del atributo (es decir ChannelName [@ id = "1"]/Name). ¿Necesitas alinearte o simplemente desempatar? –

+0

@Blaise - Solo necesito desmantelar y parece que la característica de predicado podría ser la solución, ya que necesito tomar datos de elementos en otras partes del documento. Además, he revisado tu (excelente) blog y casi he conseguido que MOXy funcione en Spring. Me tomó un tiempo entender acerca de los archivos jaxb.properties y jaxb.index. Actualmente estoy obteniendo una 'clase org.springframework.oxm.jaxb.Jaxb2Marshaller $ ByteArrayDataSource requiere una excepción de constructor de argumento cero '. Estoy usando Eclipse Persistence Services 2.3.0.v20110312-r9123 – andyb

2

Usted puede ahorrar su tiempo de codificación mediante la automatización de este proceso en JAXB:

Crear un esquema XML para su XML utilizando el siguiente enlace como guardarlo como salida.xsd archivo: http://www.xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx

ejecute el archivo de secuencia de comandos por lotes (nombre como output.bat) por debajo de la carpeta raíz del proyecto usando JDK JDK, ya que sólo tiene xjc.exe herramienta (llene el (.) detalles necesarios):

"C:\Program Files\Java\jdk1.6.0_24\bin\xjc.exe" -p %1 %2 -d %3 

donde ...

syntax: output.bat %1 %2 %3 
%1 = target package name 
%2 = full file path name of the generated XML schema .xsd 
%3 = root source folder to store generated JAXB java files 

Ejemplo:

Digamos carpeta del proyecto se organiza de la siguiente manera: (.) ​​

. 
\_src 

Ejecutar siguiente en el símbolo del sistema a partir de:

output.bat com.project.xml .\output.xsd .\src 

Se va a crear unos archivos:

. 
\_src 
    \_com 
    \_project 
     \_xml 
     |_ObjectFactory.java 
     |_Output.java 

A continuación, puede crear algunos métodos útiles a continuación para manipular objetos Output:

private JAXBContext jaxbContext = null; 
private Unmarshaller unmarshaller = null; 
private Marshaller marshaller = null; 

public OutputManager(String packageName) { 
    try { 
     jaxbContext = JAXBContext.newInstance(packageName); 
     unmarshaller = jaxbContext.createUnmarshaller(); 
     marshaller = jaxbContext.createMarshaller(); 
    } catch (JAXBException e) { 
    } 
} 

public Output loadXML(InputStream istrm) { 

    Output load = null; 

    try { 
     Object o = unmarshaller.unmarshal(istrm); 

     if (o != null) { 

      load = (Output) o; 

     } 

    } catch (JAXBException e) { 

     JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE); 

    } 
    return load; 
} 

public void saveXML(Object o, java.io.File file) { 

    Output save = null; 

    try { 
     save = (Output) o; 

     if (save != null) { 
      marshaller.marshal(save, file); 
     } 

    } catch (JAXBException e) { 

     JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE); 

    } 
} 

public void saveXML(Object o, FileOutputStream ostrm) { 

    Output save = null; 

    try { 

     save = (Output) o; 

     if (save != null) { 
      marshaller.marshal(save, ostrm); 
     } 

    } catch (JAXBException e) { 

     JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE); 

    } 
} 
+0

Esto es útil, pero no creo que vaya a resolver mi problema, ya que realmente quiero escribir el menor código posible, pero también manipular la estructura XML en tiempo de vinculación. Escribir tan poco código como sea posible no es porque soy flojo, realmente creo que es innecesario y también agrega complejidad y requiere pruebas más exhaustivas. El código simple y elegante me hace feliz. Sin embargo, +1 para la respuesta ya que no estaba al tanto del enlace o la herramienta que mencionaste. – andyb

+1

@andyb: Tal vez pueda automatizar primero y luego editar los archivos generados. – eee

Cuestiones relacionadas