2012-09-14 21 views
7

estoy usando xstream y tengo un XML de ejemplo:XML atributo de mapeo de elemento anidado

<person> 
    <firstname>Joe</firstname> 
    <lastname>Walnes</lastname> 
    <phone value="1234-456" /> 
    <fax value="9999-999" /> 
</person> 

y yo whant para asignar a la clase

public class Person { 

    private String firstname; 
    private String lastname; 
    private String phone; 
    private String fax; 

} 

Así que la idea es asociar atributo del elemento anidado al objeto actual. He intentado encontrar cualquier convertidor listo para usar sin éxito. Creo que eso es posible mediante la implementación de un nuevo convertidor, pero puede ser que alguien ya lo haya hecho. O hay una solución que no he encontrado.

Actualizado:

La idea que estoy tratando de poner en práctica es la omisión de las entidades innecesarias de haber sido creada y asignada. No necesito entidades de teléfono y fax, solo necesito sus atributos en mi modelo. El esquema XML que intento analizar es de terceros y no puedo modificarlo.

+0

Bueno manchado! He arreglado esto Fue escrito desde cero. –

+0

¿Podría aclarar un poco más lo que está buscando? Usted dice "No necesito entidades de teléfono y fax ...", bueno, no las tiene; son solo cadenas en su modelo, no entidades separadas ... ¿Desea que solo los dos primeros atributos se correlacionen o ¿los cuatro? – JoeG

+0

El problema es cómo asignar la muestra XML al modelo. Sin ningún esfuerzo adicional, XStream supuso que los elementos XML se interpretarían como miembros del modelo de todos modos (incluso se omiten explícitamente). Al tener un esquema XML enriquecido, probablemente le gustaría simplificar el modelo para escapar de los "titulares" redundantes de datos significativos. Las colecciones implícitas son buenas pero no suficientes. –

Respuesta

5

No sé de un convertidor de lista para usar que lo hará, pero es bastante trivial para escribir uno

import com.thoughtworks.xstream.converters.*; 
import com.thoughtworks.xstream.io.*; 

public class ValueAttributeConverter implements Converter { 
    public boolean canConvert(Class cls) { 
    return (cls == String.class); 
    } 

    public void marshal(Object source, HierarchicalStreamWriter w, MarshallingContext ctx) { 
    w.addAttribute("value", (String)source); 
    } 

    public Object unmarshal(HierarchicalStreamReader r, UnmarshallingContext ctx) { 
    return r.getAttribute("value"); 
    } 
} 

Puede conectar el convertidor a los campos relevantes mediante anotaciones

import com.thoughtworks.xstream.annotations.*; 

@XStreamAlias("person") 
public class Person { 

    private String firstname; 
    private String lastname; 

    @XStreamConverter(ValueAttributeConverter.class) 
    private String phone; 

    @XStreamConverter(ValueAttributeConverter.class) 
    private String fax; 

    // add appropriate constructor(s) 

    /** For testing purposes - not required by XStream itself */ 
    public String toString() { 
     return "fn: " + firstname + ", ln: " + lastname + 
      ", p: " + phone + ", f: " + fax; 
    } 
} 

para hacer este trabajo, todo lo que necesita hacer es instruir a xstream para leer las anotaciones:

XStream xs = new XStream(); 
xs.processAnnotations(Person.class); 
Person p = (Person)xs.fromXML(
    "<person>\n" + 
    " <firstname>Joe</firstname>\n" + 
    " <lastname>Walnes</lastname>\n" + 
    " <phone value='1234-456' />\n" + 
    " <fax value='9999-999' />\n" + 
    "</person>"); 
System.out.println(p); 
// prints fn: Joe, ln: Walnes, p: 1234-456, f: 9999-999 
+0

Gracias, lo comprobaré. No estoy seguro de entender cómo funciona allí. Posiblemente debido a la falta de comprensión de los convertidores en absoluto. –

+1

Cuando XStream lee XML que representa una clase de tipo de bean, lee el nombre del elemento para determinar con qué campo está tratando y luego lo delega al convertidor correspondiente. El método 'unmarshal' del convertidor se pasa como' HierarchicalStreamReader' que apunta a la etiqueta de apertura del elemento (en este caso) 'phone'. Los convertidores de valor único normal operan en el contenido de texto del elemento, pero mi convertidor personalizado en cambio mira el atributo llamado 'valor' - no necesita preocuparse por el nombre del elemento. –

4

Nota: Soy el líder EclipseLink JAXB (MOXy) y miembro del grupo de expertos JAXB (JSR-222).

Si usted está abierto a la utilización de una biblioteca que no sea xstream, a continuación es cómo se puede aprovechar la extensión @XmlPath en EclipseLink JAXB (moxy).

persona

@XmlPath La anotación le permite asignar su campo/propiedad a una ubicación en el documento XML mediante un XPath (ver: http://blog.bdoughan.com/2010/07/xpath-based-mapping.html).

package forum12425401; 

import javax.xml.bind.annotation.*; 
import org.eclipse.persistence.oxm.annotations.XmlPath; 

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

    private String firstname; 
    private String lastname; 

    @XmlPath("phone/@value") 
    private String phone; 

    @XmlPath("fax/@value") 
    private String fax; 

} 

jaxb.properties

Para especificar moxy como su proveedor de JAXB es necesario incluir un archivo llamado jaxb.properties en el mismo paquete que el modelo de dominio con la siguiente entrada (ver: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

demostración

El siguiente código convertirá el XML en su modelo de dominio y luego volverá a escribir el modelo de dominio en XML.

package forum12425401; 

import java.io.File; 
import javax.xml.bind.*; 

public class Demo { 

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

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     File xml = new File("src/forum12425401/input.xml"); 
     Person person = (Person) unmarshaller.unmarshal(xml); 

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

} 

input.xml/salida

<?xml version="1.0" encoding="UTF-8"?> 
<person> 
    <firstname>Joe</firstname> 
    <lastname>Walnes</lastname> 
    <phone value="1234-456"/> 
    <fax value="9999-999"/> 
</person> 
+0

Muchas gracias por una respuesta tan detallada. Encontré a JAXB investigando este problema y lo primero que preocupaba era a @XmlPath. ¿Podrían dar una explicación breve ahora que funciona, quiero decir que requiere que se extraigan elementos adicionales? El rendimiento y la eficiencia de los datos son los requisitos más importantes para mí por ahora. –

+1

@ViktorStolbin - '@ XmlPath' es en realidad una extensión que se proporciona en la implementación EclipseLink MOXy de la especificación JAXB (JSR-222). MOXy es compatible con un subconjunto de XPath que se puede procesar durante el primer paso de profundidad de un documento XML. Es un proceso muy eficiente. –

Cuestiones relacionadas