read - Java: deserialización de un archivo XML plano.(XStream, JAXB, MOXy, lo que sea...)
xml parsing java (2)
Actualmente estoy usando XStream para analizar un archivo XML pero no puedo hacer que haga lo que necesito que haga. Si es necesario, cambiaré a otra biblioteca, ¡cualquiera que sea la solución a este problema!
Básicamente, estoy tratando de analizar un feed XML similar a esto:
<product>
<title>Transformers Best of Grimlock</title>
<author1>Bob Budiansky</author1>
<author2>Simon Furman</author2>
</product>
Que estoy tratando de analizar en un modelo como este:
public class Product extends Model {
public String title;
public List<String> authors;
}
El título funciona muy bien, pero tengo problemas para analizar a los autores. Lamentablemente, obtener el feed XML en un formato "más sensible" como este no es una opción:
...
<authors>
<author>Bob Budiansky</author>
<author>Simon Furman</author>
</authors>
...
¿Hay algo que pueda hacer para analizar "author1", "author2", etc. en una lista?
Tenga en cuenta que soy muy nuevo en XStream (¡y que no he usado JAXB en absoluto!), Así que necesitaré indicadores sobre dónde comenzar si tengo que escribir carpetas personalizadas o lo que sea.
Gracias por leer, espero cualquier solución potencial que pueda salvar mi cordura. :)
Editar: Actualicé la publicación para mostrar que no estoy vinculado a XStream. Realmente estoy luchando por encontrar la respuesta a esto, quizás no sé qué buscar ...
El siguiente enfoque debería funcionar con cualquier biblioteca de enlace XML que pueda deshacerse de un StAX XMLStreamReader
. Lo demostraré a continuación usando las API JAXB (JSR-222) estándar. (Nota: soy el líder de EclipseLink JAXB (MOXy) ).
Producto
Anotaremos el modelo como si la propiedad de los authors
estuviera asignada al elemento author
.
package forum11666565;
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product extends Model {
public String title;
@XmlElement(name = "author")
public List<String> authors;
}
Manifestación
Analizaremos el documento XML con XMLStreamReader
. En ese XMLStreamReader
crearemos un StreamReaderDelegate
que reportará cualquier elemento que comience con el author
como un elemento llamado author
.
package forum11666565;
import java.io.FileInputStream;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Product.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("src/forum11666565/input.xml"));
xsr = new StreamReaderDelegate(xsr) {
@Override
public String getLocalName() {
String localName = super.getLocalName();
if(localName.startsWith("author")) {
return "author";
} else {
return localName;
}
}
};
Unmarshaller unmarshaller = jc.createUnmarshaller();
Product product = (Product) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(product, System.out);
}
}
input.xml
<product>
<title>Transformers: Best of Grimlock</title>
<author1>Bob Budiansky</author1>
<author2>Simon Furman</author2>
</product>
Salida
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product>
<title>Transformers: Best of Grimlock</title>
<author>Bob Budiansky</author>
<author>Simon Furman</author>
</product>
Podría transformar el XML inicial a través de XSLT antes de analizarlo con XStream:
La hoja de estilo XSLT transformará el XML como lo desee:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="product">
<xsl:element name="product">
<xsl:for-each select="*[not(starts-with(name(.), ''author''))]">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:element name="authors">
<xsl:for-each select="*[starts-with(name(.), ''author'')]">
<xsl:element name="author">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:copy select=".">
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>