使用SimpleFramework反序列化多个不同的标记(具有相同的名称)

时间:2016-03-18 09:13:24

标签: java xpath deserialization simple-framework

我正在使用Simple Framework for XML序列化/反序列化的东西,虽然第一个很容易,但我遇到了后者的问题。所以,我从服务器收到XML响应,它看起来像这样:

<?xml version="1.0" ?>
<tables>
    <table name="result" a="context" b="name">
            <r a="stuff1" b="blahblah" />
    </table>
    <table name="response" a="error" b="reason">
            <r a="0" b="" />
    </table>
</tables>

是的,它有两个名为“table”的元素。问题是第一个“table”元素可能有3个以上的属性,这意味着,我不能只为“table”标签创建一个通用实体。所以,我反序列化实体的当前代码如下所示:

@Root(name = "tables", strict = false)
public class Response {
    @Element(name = "table", required = false)
    @Path("//table[@name='result']")
    Result resultTable;

    @Element(name = "table")
    @Path("//table[@name='result']")
    Response responseTable;

    public Result getResultTable() {
        return resultTable;
    }

    public void setResultTable(Result resultTable) {
        this.resultTable = resultTable;
    }

    public Response getResponseTable() {
        return responseTable;
    }

    public void setResponseTable(Response responseTable) {
        this.responseTable = responseTable;
    }
}

不幸的是,它不起作用:我得到一个例外:

org.simpleframework.xml.core.PathException: Path '//[@name='result']' in field 'resultTable'
com.package.Response.resultTable references document root

我尝试了不同的XPath选项,比如简单地使用wildcarting节点:

@Path("//*[@name='result']")
@Path("*[@name='result']") 

但这也不起作用。因此,由于错误的XPath选项或简单框架的limitations而导致我的错误:

  

使用此类注释时需要注意的一点是,只支持XPath表达式语法的一个子集。例如,元素和属性引用不能从文档的根目录中获取,只允许在当前上下文中引用。

然后我应该使用其他XML反序列化器吗?感谢。

1 个答案:

答案 0 :(得分:1)

您可以使用内联列表尝试解决方法。另一个 - 并且在我看来更好的解决方案:使用Converter作为&#34;如果名称是结果反序列化为结果,如果响应反序列化为响应&#34; part。这听起来比实际更复杂!

由于有两个Response类 - 一个用于tables,另一个用作table,我将后者命名为ResponseTable;您的示例中ResultTableResult。我想你有一些包来防止这种情况。

对于这两个表,类Content用于对<r ... />元素进行建模。

ResponseTable

@Root
public class ResponseTable // 'Response' in your code
{
    @Attribute(name = "name")
    private String name;
    @Attribute(empty = "a")
    private String a;
    @Attribute(empty = "b")
    private String b;
    @Element(name = "r")
    private Content r;

    // ...
}

ResultTable

@Root
public class ResultTable // 'Result' in your code
{
    @Attribute(name = "name")
    private String name;
    @Attribute(empty = "a")
    private String a;
    @Attribute(empty = "b")
    private String b;
    @Element(name = "r")
    private Content r;

    // ...
}

内容

@Root()
public class Content
{
    @Attribute(name = "a")
    private String a;
    @Attribute(name = "b")
    private String b;

    // ...
}

响应

这是有趣的部分:

@Root(name = "tables", strict = false)
@Convert(Response.ResponseConverter.class)
public class Response
{
    @Element(name = "table")
    private ResultTable resultTable;
    @Element(name = "table2")
    private ResponseTable responeTable;

    // ...

    static class ResponseConverter implements Converter<Response>
    {
        private final Serializer ser = new Persister();


        @Override
        public Response read(InputNode node) throws Exception
        {
            Response resp = new Response();
            InputNode n = node.getNext();

            while( n != null )
            {
                switch( n.getAttribute("name").getValue() )
                {
                    case "result":
                        resp.resultTable = ser.read(ResultTable.class, n);
                        break;
                    case "response":
                        resp.responeTable = ser.read(ResponseTable.class, n);
                        break;
                    default:
                        throw new RuntimeException("Unsupported table: " 
                                + n.getAttribute("name").getValue());
                }

                n = node.getNext();
            }

            return resp;
        }


        @Override
        public void write(OutputNode node, Response value) throws Exception
        {
            // Implement as needed (hint: again use Serializer here)
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }
}

这里会发生什么:

  1. @Convert用于 指定Converter 实现,该实现实现{de-} Response类的序列化
  2. 实施ResponseConverter仅用于 确定<table …>…</table>元素的类型 (=响应或结果表)
  3. 对于实际的反序列化,使用了正常的 Serializer - 无需手动完成整个工作!
  4. 我还没有实施write()部分,但正如你所看到的那样并不困难;您可以再次使用Serializer来完成主要工作。

    总结Converter

    • 如果表格包含属性名称=回复:反序列化为Response
    • 如果属性名称=结果的表格:反序列化为Result
    • 其他:投掷执行

    这使得可以有多个序列化为xml的类,即使它们共享相同的元素名称。

    用法

    只需要注意一件事:对于@Convert,必须设置AnnotationStrategy

    Serializer ser = new Persister(new AnnotationStrategy());
                                   //  ^^^^^^^^^^^^^^^^^^^^
    final String xml = …
    
    Response response = ser.read(Response.class, xml);
    System.out.println(response)
    

    注意: 您无法在AnnotationStrategy内使用Converter策略 - 只要你不依赖另一个Converter那里。

    <强>输出

    (生成toString()方法)

    Response{resultTable=ResultTable{name=result, a=context, b=name, r=Content{a=stuff1, b=blahblah}}, responeTable=ResponseTable{name=response, a=error, b=reason, r=Content{a=0, b=}}}