The first step to start using XStream is to include a reference to it in the maven pom file:
<dependency>
<groupid>com.thoughtworks.xstream</groupid>
<artifactid>xstream</artifact>
<version>1.3</version>
</dependency>
The idea of XStream is to annotate your domain classes with xstream annotations which map the attributes and references to XML. In our case, we were going in the other direction: from XML to objects and in that case, if the structure of the XML is reasonably small it makes sense to define the objects inside one 'mother' class. For example say we have an XML:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<book id="13">
<author>Robert C. Martin</author>
<title>Clean Code</title>
</book>
</records>
This datastructure can be mapped by the following (inner) classes like so (omitting the imports)
public class XStreamDemonstrator {
public static void main(String[] args) throws Exception {
XStream stream = new XStream();
stream.processAnnotations(Records.class);
FileInputStream is = new FileInputStream("resources/books.xml");
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
Records records = (Records) stream.fromXML(isr);
for (Book book : records.books) {
System.out.println("Book " + book.id + ": " + book.title + " by " + book.author);
}
}
@XStreamAlias("records")
public static class Records {
@XStreamImplicit
List<Book> books;
}
@XStreamAlias("book")
public static class Book {
@XStreamAlias("id")
@XStreamAsAttribute
String id;
String author;
String title;
}
}
Now how's that for simplicity? It's fast, easy and it works. That's right; but now suppose the book gets translated and we'd like to add an extra attribute to the XML:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<book id="13">
<author>Robert C. Martin</author>
<title language="en">Clean Code</title>
</book>
</records>
The problem lies in the added attribute 'language'. There is no way to map this with XStream annotations as is, this is also confirmed in the newsgroup. The solution is to use a Converter implementation class which reads out both the attribute and the child text at once:
@XStreamAlias("book")
public static class Book {
@XStreamAlias("id")
@XStreamAsAttribute
String id;
String author;
@XStreamConverter(TitleLanguageConverter.class)
TitleLanguage title;
}
public static class TitleLanguage {
String title;
String language;
public TitleLanguage(String title, String language) {
this.title = title;
this.language = language;
}
}
public static class TitleLanguageConverter implements Converter {
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
return new TitleLanguage(reader.getValue(), reader.getAttribute("language"));
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
// not implemented
}
public boolean canConvert(Class type) {
return type.equals(TitleLanguage.class);
}
}
That wasn't too bad...that is...there is one small problem with the above code because it throws the following stack trace upon execution:
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: only START_TAG can have attributes END_TAG seen ...Robert C. Martin ... @4:43 : only START_TAG can have attributes END_TAG seen ...Robert C. Martin ... @4:43
---- Debugging information ----
message : only START_TAG can have attributes END_TAG seen ...Robert C. Martin ... @4:43
cause-exception : java.lang.IndexOutOfBoundsException
cause-message : only START_TAG can have attributes END_TAG seen ...Robert C. Martin ... @4:43
class : XStreamDemonstrator$Records
required-type : XStreamDemonstrator$TitleLanguage
path : /records/book/author
line number : 4
-------------------------------
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:88)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:55)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:75)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshallField(AbstractReflectionConverter.java:234)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:206)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:150)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:81)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:55)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:75)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:59)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:213)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:150)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:81)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:55)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:75)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:59)
at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:142)
at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:33)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:931)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:917)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:861)
at XStreamDemonstrator.main(XStreamDemonstrator.java:30)
Caused by: java.lang.IndexOutOfBoundsException: only START_TAG can have attributes END_TAG seen ...Robert C. Martin ... @4:43
at org.xmlpull.mxp1.MXParser.getAttributeValue(MXParser.java:927)
at com.thoughtworks.xstream.io.xml.XppReader.getAttribute(XppReader.java:93)
at com.thoughtworks.xstream.io.ReaderWrapper.getAttribute(ReaderWrapper.java:52)
at XStreamDemonstrator$TitleLanguageConverter.unmarshal(XStreamDemonstrator.java:65)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:81)
... 21 more
It turns out that the order is important in TitleLanguageConverter.unmarshal: when we first read the book title text, the XML cursor has already moved past the language attribute and cannot go back to read it. The solution is to reverse the order:
return new TitleLanguage(reader.getAttribute("language"), reader.getValue());
And everything works as expected!
1 opmerking:
BetMGM, Wynn Restaurants Near Las Vegas - JTM Hub
J-T Best Places 당진 출장마사지 To Go. Best Las 영주 출장마사지 Vegas Hotels With J-T Casinos. J.T 광주 출장마사지 Best Restaurants With J-T 천안 출장안마 Casinos. J.T Best Places To Go. Las Vegas. Las Vegas. 서귀포 출장샵 Las Vegas. J.T Best Restaurants With
Een reactie posten