Reputation: 187
I'm currently using XStream to parse an XML file but can't get it to do what I need it to do. I will change to another library if necessary, whatever can solve this problem!
Basically I'm trying to parse an XML feed similar to this:
<product>
<title>Transformers Best of Grimlock</title>
<author1>Bob Budiansky</author1>
<author2>Simon Furman</author2>
</product>
Which I'm trying to parse into a model like this:
public class Product extends Model {
public String title;
public List<String> authors;
}
The title works beautifully but I'm having difficulty parsing the authors. Unfortunately getting the XML feed in a "more sensible" format like this is not an option:
...
<authors>
<author>Bob Budiansky</author>
<author>Simon Furman</author>
</authors>
...
Is there anything I can do to parse "author1", "author2", etc into a list?
Please bear in mind I'm very new to XStream (and haven't used JAXB at all!) so will need pointers on where to start if I've to write any custom binders or whatever.
Thanks for reading, I look forward to any potential solutions that may save my sanity! :)
Edit: I've updated the post to show I'm not tied to XStream. Really struggling to find the answer to this - perhaps I just don't know what to search for...
Upvotes: 3
Views: 1556
Reputation: 149017
The following approach should work with any XML-binding library that can unmarshal from a StAX XMLStreamReader
. I'll demonstrate below using the standard JAXB (JSR-222) APIs. (Note: I'm the EclipseLink JAXB (MOXy) lead).
Product
We'll annotate the model as if the authors
property is mapped to the author
element.
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;
}
Demo
We'll parse the XML document with an XMLStreamReader
. On that XMLStreamReader
we'll create a StreamReaderDelegate
that will report any elements starting with author
as an element called 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>
Output
<?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>
Upvotes: 3
Reputation: 35018
You could transform the initial XML via XSLT before parsing it with XStream:
The XSLT stylesheet would transform the XML as you want:
<?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>
Upvotes: 2