问题
I have an XML document with many Entity
elements, which each have an attribute of either type="foo"
or type="bar"
. See this sample:
<RootNode>
<Entities>
<Entity type="foo">
<Price>1</Price>
</Entity>
<Entity type="bar">
<URL>www.google.co.uk</URL>
</Entity>
<Entity type="foo">
<Price>77</Price>
</Entity>
</Entities>
</RootNode>
I need a way to tell Simple to deserialize the Entity
elements with type="foo"
into a List<FooEntity>
and the elements with type="bar"
into a List<BarEntity>
.
How can I do this?
Here is the code I currently have if you want to play around with it:
public class App {
public static void main(String[] args) throws Exception {
Reader r = Files.newBufferedReader(
Paths.get("/path/to/file.xml"),
Charset.defaultCharset()
);
Serializer serializer = new Persister();
RootNode root = serializer.read(RootNode.class, r);
System.out.println(root.getFooEntities().size());
System.out.println(root.getBarEntities().size());
}
}
@Root(name = "RootNode")
class RootNode {
// TODO: What annotations to put here?
private List<FooEntity> fooEntities;
// TODO: What annotations to put here?
private List<BarEntity> barEntities;
public List<FooEntity> getFooEntities() { return fooEntities; }
public List<BarEntity> getBarEntities() { return barEntities; }
}
class FooEntity {
@Element(name = "URL")
private String url;
}
class BarEntity {
@Element(name = "Price")
private int price;
}
回答1:
Now the real problem ...
// TODO: What annotations to put here?
private List<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")
public class FooEntity
{
@Attribute(name = "type")
private String type;
@Element(name = "Price")
private int price;
/*
* NOTE: A default ctor is required - visibile doesn't matter
*/
@Override
public String toString()
{
return "FooEntity{" + "price=" + price + '}';
}
}
Class BarEntity
@Root(name = "Entity")
public class BarEntity
{
@Attribute(name = "type")
private String type;
@Element(name = "URL")
private String url;
/*
* NOTE: A default ctor is required - visibile doesn't matter
*/
@Override
public String toString()
{
return "BarEntity{" + "url=" + url + '}';
}
}
Class RootNode
@Root(name = "RootNode")
@Convert(RootNodeConverter.class) // <--- Important!
class RootNode
{
private List<FooEntity> fooEntities;
private List<BarEntity> barEntities;
public RootNode()
{
// This has to be done somewhere ...
this.fooEntities = new ArrayList<>();
this.barEntities = new ArrayList<>();
}
public List<FooEntity> getFooEntities()
{
return fooEntities;
}
public List<BarEntity> getBarEntities()
{
return barEntities;
}
@Override
public String toString()
{
return "RootNode{" + "fooEntities=" + fooEntities + ", barEntities=" + barEntities + '}';
}
}
And finally the Converter
-implementation:
Class RootNodeConverter
public class RootNodeConverter implements Converter<RootNode>
{
@Override
public RootNode read(InputNode node) throws Exception
{
RootNode root = new RootNode();
final InputNode entities = node.getNext("Entities");
InputNode child;
while( ( child = entities.getNext() ) != null )
{
if( child.getName().equals("Entity") == false )
{
continue; // Not an Entity
}
final Serializer ser = new Persister();
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;
}
@Override
public void write(OutputNode node, RootNode value) throws Exception
{
throw new UnsupportedOperationException("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:
final String 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>";
final Serializer 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}]}
来源:https://stackoverflow.com/questions/25854774/deserialize-xml-elements-to-different-types-based-on-attribute