首页 > 解决方案 > 简单 XML 框架:回退到未知枚举值的默认值

问题描述

假设一个 XML提要由多个消息组成,例如:

<feed>
    <message type="FOO_TYPE">
        <!-- possibly some message content here -->
    </message>
    <!-- more messages -->
</feed>

feedmessage分别被翻译成两个不同的Java类;type被翻译成 Java enum

反序列化代码:

// custom strategy to avoid name collisions (we use `class` as an attribute name)
Strategy XML_STRATEGY = new TreeStrategy("simpleXmlClass", "simpleXmlLength");
Serializer XML_SERIALIZER = new Persister(XML_STRATEGY);
Feed feed = XML_SERIALIZER.read(Feed.class, inputStream);

现在,如果提要包含一条未知的消息type(即未为相应的 定义的消息enum),则会引发异常。如果我将调用包装Serializer#read()到异常处理程序中,这将导致整个提要被丢弃。

我想要实现的是在遇到未知值时替换默认值(或可能为 null)(并可能在更高的地方进行一些进一步的过滤,但目前超出了范围)。但是,我不知道在哪里将该逻辑插入Strategy/Serializer构造,并且文档也没有多大帮助。

一个典型的异常如下所示:

java.lang.IllegalArgumentException: No enum constant org.traffxml.traff.TraffEvent.Type.CONSTRUCTION_BRIDGE_DEMOLITION
    at java.lang.Enum.valueOf(Enum.java:238)
    at org.simpleframework.xml.transform.EnumTransform.read(EnumTransform.java:58)
    at org.simpleframework.xml.transform.EnumTransform.read(EnumTransform.java:29)
    at org.simpleframework.xml.transform.Transformer.read(Transformer.java:106)
    at org.simpleframework.xml.core.Support.read(Support.java:372)
    at org.simpleframework.xml.core.PrimitiveFactory.getInstance(PrimitiveFactory.java:105)
    at org.simpleframework.xml.core.Primitive.readTemplate(Primitive.java:231)
    at org.simpleframework.xml.core.Primitive.read(Primitive.java:171)
    at org.simpleframework.xml.core.Primitive.read(Primitive.java:126)
    at org.simpleframework.xml.core.Composite.readVariable(Composite.java:623)
    at org.simpleframework.xml.core.Composite.readInstance(Composite.java:573)
    at org.simpleframework.xml.core.Composite.readAttribute(Composite.java:497)
    at org.simpleframework.xml.core.Composite.readAttributes(Composite.java:413)
    at org.simpleframework.xml.core.Composite.access$300(Composite.java:59)
    at org.simpleframework.xml.core.Composite$Injector.read(Composite.java:1432)
    at org.simpleframework.xml.core.Composite.read(Composite.java:201)
    at org.simpleframework.xml.core.Composite.read(Composite.java:148)
    at org.simpleframework.xml.core.Traverser.read(Traverser.java:92)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:190)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:167)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:124)
    at org.simpleframework.xml.core.Composite.readVariable(Composite.java:623)
    at org.simpleframework.xml.core.Composite.readInstance(Composite.java:573)
    at org.simpleframework.xml.core.Composite.readUnion(Composite.java:549)
    at org.simpleframework.xml.core.Composite.readElement(Composite.java:532)
    at org.simpleframework.xml.core.Composite.readElements(Composite.java:445)
    at org.simpleframework.xml.core.Composite.readSection(Composite.java:327)
    at org.simpleframework.xml.core.Composite.readElements(Composite.java:443)
    at org.simpleframework.xml.core.Composite.access$400(Composite.java:59)
    at org.simpleframework.xml.core.Composite$Injector.read(Composite.java:1433)
    at org.simpleframework.xml.core.Composite.read(Composite.java:201)
    at org.simpleframework.xml.core.Composite.read(Composite.java:148)
    at org.simpleframework.xml.core.Traverser.read(Traverser.java:92)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:190)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:167)
    at org.simpleframework.xml.core.CompositeInlineList.read(CompositeInlineList.java:124)
    at org.simpleframework.xml.core.Composite.readVariable(Composite.java:623)
    at org.simpleframework.xml.core.Composite.readInstance(Composite.java:573)
    at org.simpleframework.xml.core.Composite.readUnion(Composite.java:549)
    at org.simpleframework.xml.core.Composite.readElement(Composite.java:532)
    at org.simpleframework.xml.core.Composite.readElements(Composite.java:445)
    at org.simpleframework.xml.core.Composite.access$400(Composite.java:59)
    at org.simpleframework.xml.core.Composite$Injector.read(Composite.java:1433)
    at org.simpleframework.xml.core.Composite.read(Composite.java:201)
    at org.simpleframework.xml.core.Composite.read(Composite.java:148)
    at org.simpleframework.xml.core.Traverser.read(Traverser.java:92)
    at org.simpleframework.xml.core.Persister.read(Persister.java:625)
    at org.simpleframework.xml.core.Persister.read(Persister.java:606)
    at org.simpleframework.xml.core.Persister.read(Persister.java:584)
    at org.simpleframework.xml.core.Persister.read(Persister.java:543)
    at com.example.myowncode.MyClass.doStuff(MyClass.java:42)

由于堆栈中的最后一个异常包括一些 from EnumTransformer,它是 的实现Transform<Enum>,我想知道是否可以将我的自定义Transform类型插入到代码中;我只是不知道如何实现。

标签: javasimple-framework

解决方案


假设枚举名为 MyEnum,首先为它编写一个转换类。该类必须实现Transform<MyEnum>;我们将MyEnumTransform在这里调用它。实现这样的自定义read方法:

public MyEnum read(String value) throws Exception {
    try {
        return MyEnum.valueOf(value);
    } catch (IllegalArgumentException e) {
        return MyEnum.FALLBACK_VALUE; // or null, at your option
    }
}

您还需要实施write,您可以直接复制EnumTransform

public String write(MyEnum value) throws Exception {
    return value.name();
}

现在改变你的反序列化代码如下:

// custom strategy to avoid name collisions (we use `class` as an attribute name)
RegistryMatcher XML_MATCHER = new RegistryMatcher();
XML_MATCHER.bind(MyEnum.class, MyEnumTransform.class);
Strategy XML_STRATEGY = new TreeStrategy("simpleXmlClass", "simpleXmlLength");
Serializer XML_SERIALIZER = new Persister(XML_STRATEGY, XML_MATCHER);
Feed feed = XML_SERIALIZER.read(Feed.class, inputStream);

本质上,您正在为您的序列化程序提供自定义匹配器。否则,序列化程序将回退到一个EmptyMatcher实例(动态创建),该实例返回 null 作为任何内容的转换,导致 Simple XML 回退到该类型的默认转换(并在遇到非法值时遇到异常一个枚举)。

然后,您可能需要在反序列化具有 null/默认类型的消息时进行一些进一步的一致性检查(例如完全丢弃消息),但这超出了这里的范围。


推荐阅读