反序列化XML元素到不同的类型,根据属性 [英] Deserialize XML elements to different types, based on attribute
问题描述
我有许多实体
元素,每个要么键入=富
或键入=栏
。请参见本示例:
<&的RootNode GT;
<实体>
<实体类型=foo的>
<价格> 1 LT; /价格>
< /实体GT; <实体类型=酒吧>
< URL> www.google.co.uk< / URL>
< /实体GT; <实体类型=foo的>
<价格> 77°; /价格>
< /实体GT;
< /实体>
< /&的RootNode GT;
我需要一种方法来告诉简单反序列化实体
与键入=富
到列表< FooEntity>
和元素键入=栏
到列表与LT; BarEntity方式>
我怎样才能做到这一点?
下面是code,如果你想玩弄它,我目前有:
公共类应用{
公共静态无效的主要(字串[] args)抛出异常{
读者R = Files.newBufferedReader(
Paths.get(/路径/要/ file.xml),
Charset.defaultCharset()
);
串行串行=新的持留(); 的RootNode根= serializer.read(RootNode.class,R);
的System.out.println(root.getFooEntities()大小());
的System.out.println(root.getBarEntities()大小());
}
}@root(NAME =的RootNode)
一流的RootNode { // TODO:放什么注解吗?
私人列表< FooEntity> fooEntities; // TODO:放什么注解吗?
私人列表< BarEntity> barEntities; 公开名单< FooEntity> getFooEntities(){返回fooEntities; }
公开名单< BarEntity> getBarEntities(){返回barEntities; }
}类FooEntity {
@Element(NAME =URL)
私人字符串URL;
}类BarEntity {
@Element(NAME =价格)
私人诠释价格;
}
现在真正的问题...
// TODO:放什么注解吗?
私人列表< BarEntity> barEntities;
我的回答是:无!或者至少,不要紧!
属性,如键入
在这里仅仅是字符串,并且不能作出任何决定。但还有另一种不错的方式:
- 实施为
的RootNode
A转换器,它做的决定 - 使用一个
串行
做反序列化每个实体的实际工作。
我做了一些修改你的类,但没有壮观的已被更改。在的toString()
-method仅供测试 - 实现你需要它
类 FooEntity
@root(名称=实体)
公共类FooEntity
{
@Attribute(名称=类型)
私人字符串类型;
@Element(NAME =价格)
私人诠释价格; / *
*注意:需要一个默认构造函数 - 分享范围无所谓
* / @覆盖
公共字符串的toString()
{
回归FooEntity {+价格=+价格+'};
}
}
类 BarEntity
@root(名称=实体)
公共类BarEntity
{
@Attribute(名称=类型)
私人字符串类型;
@Element(NAME =URL)
私人字符串URL; / *
*注意:需要一个默认构造函数 - 分享范围无所谓
* / @覆盖
公共字符串的toString()
{
回归BarEntity {+URL =+ URL +'};
}
}
类的RootNode
@root(NAME =的RootNode)
@Convert(RootNodeConverter.class)//< ---重要!
一流的RootNode
{
私人列表< FooEntity> fooEntities;
私人列表< BarEntity> barEntities;
公众的RootNode()
{
//这在某处做...
this.fooEntities =新的ArrayList<>();
this.barEntities =新的ArrayList<>();
}
公开名单< FooEntity> getFooEntities()
{
返回fooEntities;
} 公开名单< BarEntity> getBarEntities()
{
返回barEntities;
} @覆盖
公共字符串的toString()
{
回归的RootNode {+fooEntities =+ fooEntities +,barEntities =+ barEntities +'};
}
}
和最后的转换
- 实施:
类 RootNodeConverter
公共类RootNodeConverter实现转换器和LT;的RootNode>
{
@覆盖
公众的RootNode阅读(InputNode节点)抛出异常
{
的RootNode根=新的RootNode();
最后InputNode实体= node.getNext(实体);
InputNode孩子; 而((孩子= entities.getNext())!= NULL)
{
如果(child.getName()。等于(实体)== FALSE)
{
继续; //不是一个实体
} 最后的串行器SER =新的持留(); 开关(child.getAttribute(类型)。的getValue())
{
案富:
。root.getFooEntities()加(ser.read(FooEntity.class,儿童));
打破;
案栏:
。root.getBarEntities()加(ser.read(BarEntity.class,儿童));
打破;
默认:
//不是一个富,也不是酒吧 - 现在该怎么办!?
打破;
}
} 返回根;
}
@覆盖
公共无效写入(OutputNode节点的RootNode值)抛出异常
{
抛出新UnsupportedOperationException异常(尚未实现!);
}
}
有一些事情要优化,例如。添加 root.addBar(ser.read(BarEntity.class,儿童))
或一般ErrorHandling中。
顺便说一句。代替的两个的列表,你可以保持的单的一(如果相关)。只要为实体的超类。您可以移动键入
-attribute到那儿去。
用法
下面是一个例子如何使用:
最终字符串输入=<&的RootNode GT; \\ N
+<实体> \\ N
+<实体类型= \\福\\> \\ N
+<价格> 1 LT; /价格> \\ n
+< /实体GT; \\ N
+\\ n
+<实体类型= \\栏\\> \\ N
+< URL> www.google.co.uk< / URL> \\ N
+< /实体GT; \\ N
+\\ n
+<实体类型= \\福\\> \\ N
+<价格> 77°; /价格> \\ n
+< /实体GT; \\ N
+< /实体> \\ N
+&下; /的RootNode>中;
最后的串行器SER =新的持留(新AnnotationStrategy()); //< - 注意咯!的RootNode根= ser.read(RootNode.class,输入);
的System.out.println(根);
真的没什么壮观这里太...
输出:
的RootNode {fooEntities = [FooEntity {价格= 1},{FooEntity价格= 77}],barEntities = [BarEntity {URL = www.google.co.uk}]}
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;
}
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}]}
这篇关于反序列化XML元素到不同的类型,根据属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!