简单XML框架:对于未知的枚举值,回退到默认值

huangapple go评论72阅读模式
英文:

Simple XML Framework: fall back to default for unknown Enum values

问题

假设一个包含多个消息的XML *feed*如下所示

    <feed>
        <message type="FOO_TYPE">
            <!-- 可能有一些消息内容 -->
        </message>
        <!-- 更多消息 -->
    </feed>

`feed``message` 分别被翻译成不同的Java类;`type` 被翻译成Java的 `enum`。

反序列化代码

	// 自定义策略以避免名称冲突(我们使用`class` 作为属性名)
	Strategy XML_STRATEGY = new TreeStrategy("simpleXmlClass", "simpleXmlLength");
	Serializer XML_SERIALIZER = new Persister(XML_STRATEGY);
	Feed feed = XML_SERIALIZER.read(Feed.class, inputStream);

现在如果feed包含一个未知的 `type`(即未在相应的 `enum` 中定义的值),将会抛出异常如果我将对 `Serializer#read()` 的调用包装到异常处理程序中整个feed将被丢弃

我想要实现的目标是在遇到未知值时替换为默认值或可能为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)
    	...

由于堆栈中的最后一些异常来自于 `EnumTransformer`,它是 `Transform<Enum>` 的一个实现我想知道是否可以为该特定枚举插入自定义的 `Transform` 类型只是我不知道如何实现

以上是您提供的内容的翻译。如果您需要更多的帮助,请随时提问。

英文:

Assume an XML feed consisting of multiple messages, such as:

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

feed and message get translated into two different Java classes, respectively; type gets translated to a Java enum.

Deserialization code:

// 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);

Now if the feed contains a single message with an unknown type (i.e. one that is not defined for the respective enum), an exception will be thrown. If I wrap the call to Serializer#read() into an exception handler, that will cause the whole feed to be discarded.

What I want to achieve is to substitute a default value (or possibly null) whenever an unknown value is encountered (and possibly do some further filtering higher up, but that is out of scope for now). However, I don’t know where to plug that logic into the Strategy/Serializer construct, and the docs do not help much either.

A typical exception looks like this:

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)

Since the last exceptions in the stack include some from EnumTransformer, which is an implementation of Transform<Enum>, I was wondering if I could plug my custom Transform type for that particular enum into the code; I jut don’t know how to achieve that.

答案1

得分: 1

假设枚举名为MyEnum,首先为其编写一个转换类。该类必须实现Transform<MyEnum>接口;我们在这里称之为MyEnumTransform。实现一个自定义的read方法如下:

public MyEnum read(String value) throws Exception {
    try {
        return MyEnum.valueOf(value);
    } catch (IllegalArgumentException e) {
        return MyEnum.FALLBACK_VALUE; // 或者根据需要返回 null
    }
}

您还需要实现write方法,您可以直接从EnumTransform中复制:

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

现在按照以下方式更改您的反序列化代码:

// 自定义策略以避免名称冲突(我们使用`class`作为属性名称)
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/默认类型的消息时进行一些进一步的一致性检查(例如,将消息全部丢弃),但这超出了此处的范围。

英文:

Assuming the enum is called MyEnum, first write a transform class for it. The class must implement Transform&lt;MyEnum&gt; ; we will call it MyEnumTransform here. Implement a custom read method like this:

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

You also need to implement write, which you can copy straight from EnumTransform:

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

Now change your deserialization code as follows:

// 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(&quot;simpleXmlClass&quot;, &quot;simpleXmlLength&quot;);
Serializer XML_SERIALIZER = new Persister(XML_STRATEGY, XML_MATCHER);
Feed feed = XML_SERIALIZER.read(Feed.class, inputStream);

Essentially you are providing a custom matcher to your serializer. Without that, the serializer will fall back to an EmptyMatcher instance (created on the fly), which returns null as the transform for anything, causing Simple XML to fall back to default transforms for the type (and hit an exception upon encountering an illegal value for an enum).

You may then need to do some further consistency checks upon deserializing a message with a null/default type (such as throwing out the message altogether), but that is out of scope here.

huangapple
  • 本文由 发表于 2020年10月5日 02:22:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/64198322.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定