I am trying deserialize list of xml elements into Map with id attribute as a key and string which sits inside element as a value. Sadly, there is no shared root element for translation elements collection which could be then aliased to Map instance. With Collections there is addImplicitCollection method which solves this problem, is there any corresponding solution for Maps?
There's the snippet that hopefully will explain more accurately what I am trying to achieve:
import com.thoughtworks.xstream.XStream;
import java.io.FileNotFoundException;
import java.util.Map;
import static org.junit.Assert.*;
import org.junit.Test;
public class XStreamMapDeserializationTest {
public static class Entry {
private String id;
Map<String, Translation> translations;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Translation> getTranslations() {
return translations;
}
public void setTranslations(Map<String, Translation> translations) {
this.translations = translations;
}
}
public static class Translation {
private String id;
private String text;
public String getId() {
return id;
}
public String getText() {
return text;
}
public void setId(String id) {
this.id = id;
}
public void setText(String text) {
this.text = text;
}
}
@Test
public void testUnmarshall_shouldProperlyUnmarshallSingleEntry() {
//given
String xmlToUnmarshall
= "<entry id=\"0\">\n"
+ " <translation id=\"0\">\n"
+ " <text>autumn</text>\n"
+ " </translation>\n"
+ " <translation id=\"1\">\n"
+ " <text>der Herbst</text>\n"
+ " </translation>\n"
+ "</entry>";
XStream xstream = new XStream();
xstream.alias("entry", Entry.class);
xstream.alias("translation", Translation.class);
//when
Entry result = (Entry) xstream.fromXML(xmlToUnmarshall);
//then
assertNotNull("Entry should not be equal null", result);
}
}
I ended up with implementing converter for Entry class, each time it finds translation node it deserializes it and adds to map, and in the end sets created Map as Entry field.
class EntryConverter implements Converter {
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Entry entry = new Entry();
Map<String, Translation> translations = new HashMap<>();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
if ("translation".equalsIgnoreCase(nodeName)) {
String id = reader.getAttribute("id");
Translation translation = (Translation) context.convertAnother(null, Translation.class);
translations.put(id, translation);
}
reader.moveUp();
}
entry.setTranslations(translations);
return entry;
}
@Override
public boolean canConvert(Class type) {
return Entry.class.isAssignableFrom(type);
}
}
then I just added converter to xstream configuration:
xstream.registerConverter(new EntryConverter());