反序列化XML元素到不同的类型,根据属性 [英] Deserialize XML elements to different types, based on attribute

查看:179
本文介绍了反序列化XML元素到不同的类型,根据属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有许多实体元素,每个要么键入=富或键入=栏。请参见本示例:

 <&的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;

我的回答是:无!或者至少,不要紧!

属性,如键入在这里仅仅是字符串,并且不能作出任何决定。但还有另一种不错的方式:


  1. 实施为的RootNode A转换器,它做的决定

  2. 使用一个串行做反序列化每​​个实体的实际工作。

我做了一些修改你的类,但没有壮观的已被更改。在的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:

  1. Implement a Converter for RootNode which does the decision
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆