英文:
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<MyEnum>
; 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("simpleXmlClass", "simpleXmlLength");
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论