简单 XML 框架:回退到未知枚举值的默认值
Simple XML Framework: fall back to default for unknown Enum values
假设一个 XML 提要 由多个 消息 组成,例如:
<feed>
<message type="FOO_TYPE">
<!-- possibly some message content here -->
</message>
<!-- more messages -->
</feed>
feed
和message
分别被翻译成两个不同的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);
现在,如果 Feed 包含一条带有未知 type
的消息(即未为相应的 enum
定义的消息),将抛出异常。如果我将对 Serializer#read()
的调用包装到一个异常处理程序中,那将导致整个提要被丢弃。
我想要实现的是每当遇到未知值时替换默认值(或可能为空)(并可能在更高的地方做一些进一步的过滤,但现在超出范围)。但是,我不知道在哪里将该逻辑插入到 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.access0(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.access0(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.access0(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)
由于堆栈中的最后一个异常包括来自 EnumTransformer
的一些异常,它是 Transform<Enum>
的一个实现,我想知道我是否可以为那个特定的类型插入我的自定义 Transform
类型枚举到代码中;我只是不知道如何实现。
假设枚举名为MyEnum,首先为它编写一个转换class。 class 必须实现 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
实例(动态创建),其中 returns null 作为任何转换,导致简单 XML 回退到默认转换类型(并在遇到非法的枚举值时抛出异常)。
然后,您可能需要在反序列化具有 null/default 类型的消息时进行一些进一步的一致性检查(例如完全丢弃消息),但这超出了此处的范围。
假设一个 XML 提要 由多个 消息 组成,例如:
<feed>
<message type="FOO_TYPE">
<!-- possibly some message content here -->
</message>
<!-- more messages -->
</feed>
feed
和message
分别被翻译成两个不同的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);
现在,如果 Feed 包含一条带有未知 type
的消息(即未为相应的 enum
定义的消息),将抛出异常。如果我将对 Serializer#read()
的调用包装到一个异常处理程序中,那将导致整个提要被丢弃。
我想要实现的是每当遇到未知值时替换默认值(或可能为空)(并可能在更高的地方做一些进一步的过滤,但现在超出范围)。但是,我不知道在哪里将该逻辑插入到 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.access0(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.access0(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.access0(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)
由于堆栈中的最后一个异常包括来自 EnumTransformer
的一些异常,它是 Transform<Enum>
的一个实现,我想知道我是否可以为那个特定的类型插入我的自定义 Transform
类型枚举到代码中;我只是不知道如何实现。
假设枚举名为MyEnum,首先为它编写一个转换class。 class 必须实现 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
实例(动态创建),其中 returns null 作为任何转换,导致简单 XML 回退到默认转换类型(并在遇到非法的枚举值时抛出异常)。
然后,您可能需要在反序列化具有 null/default 类型的消息时进行一些进一步的一致性检查(例如完全丢弃消息),但这超出了此处的范围。