Deserialize Xml Elements To Different Types, Based On Attribute
I have an XML document with many Entity elements, which each have an attribute of either type='foo' or type='bar'. See this sample: &l
Solution 1:
Now the real problem ...
// TODO: What annotations to put here?privateList<BarEntity> barEntities;
My answer: none! Or at least, it doesn't matter!
Attributes like type
here are just strings and can't make any decisions. But there's another nice way:
- Implement a Converter for
RootNode
which does the decision - Use a
Serializer
to do the actual work of deserializing each entity.
I made some modifications to your classes, but nothing spectacular has been changed. The toString()
-method is for testing only - implement as you need it.
Class FooEntity
@Root(name = "Entity")
publicclassFooEntity
{
@Attribute(name = "type")
privateStringtype;
@Element(name = "Price")
private int price;
/*
* NOTE: A default ctor is required - visibile doesn't matter
*/@OverridepublicStringtoString()
{
return"FooEntity{" + "price=" + price + '}';
}
}
Class BarEntity
@Root(name = "Entity")
publicclassBarEntity
{
@Attribute(name = "type")
privateStringtype;
@Element(name = "URL")
privateString url;
/*
* NOTE: A default ctor is required - visibile doesn't matter
*/@OverridepublicStringtoString()
{
return"BarEntity{" + "url=" + url + '}';
}
}
Class RootNode
@Root(name = "RootNode")
@Convert(RootNodeConverter.class) // <--- Important!classRootNode
{
privateList<FooEntity> fooEntities;
privateList<BarEntity> barEntities;
publicRootNode()
{
// This has to be done somewhere ...this.fooEntities = newArrayList<>();
this.barEntities = newArrayList<>();
}
publicList<FooEntity> getFooEntities()
{
return fooEntities;
}
publicList<BarEntity> getBarEntities()
{
return barEntities;
}
@OverridepublicStringtoString()
{
return"RootNode{" + "fooEntities=" + fooEntities + ", barEntities=" + barEntities + '}';
}
}
And finally the Converter
-implementation:
Class RootNodeConverter
publicclassRootNodeConverterimplementsConverter<RootNode>
{
@Overridepublic RootNode read(InputNode node)throws Exception
{
RootNoderoot=newRootNode();
finalInputNodeentities= node.getNext("Entities");
InputNode child;
while( ( child = entities.getNext() ) != null )
{
if( child.getName().equals("Entity") == false )
{
continue; // Not an Entity
}
finalSerializerser=newPersister();
switch(child.getAttribute("type").getValue())
{
case"foo":
root.getFooEntities().add(ser.read(FooEntity.class, child));
break;
case"bar":
root.getBarEntities().add(ser.read(BarEntity.class, child));
break;
default:
// Not a Foo nor a Bar - what now!?break;
}
}
return root;
}
@Overridepublicvoidwrite(OutputNode node, RootNode value)throws Exception
{
thrownewUnsupportedOperationException("Not implemented yet!");
}
}
There are some things to optimize, eg. add a root.addBar(ser.read(BarEntity.class, child))
or errorhandling in general.
Btw. instead of two lists, you can maintain a single one (if relevant). Just make a superclass for the entities. You can move the type
-attribute to there too.
Usage
Here's an example how to use:
finalString input ="<RootNode>\n"+" <Entities>\n"+" <Entity type=\"foo\">\n"+" <Price>1</Price>\n"+" </Entity>\n"+"\n"+" <Entity type=\"bar\">\n"+" <URL>www.google.co.uk</URL>\n"+" </Entity>\n"+"\n"+" <Entity type=\"foo\">\n"+" <Price>77</Price>\n"+" </Entity>\n"+" </Entities>\n"+"</RootNode>";
finalSerializer ser = new Persister(new AnnotationStrategy()); // <-- Note the strategy!RootNode root = ser.read(RootNode.class, input);
System.out.println(root);
Nothing really spectacular here too ...
Output:
RootNode{fooEntities=[FooEntity{price=1}, FooEntity{price=77}], barEntities=[BarEntity{url=www.google.co.uk}]}
Post a Comment for "Deserialize Xml Elements To Different Types, Based On Attribute"