java - 具有兄弟类型的联合对象的杰克逊反序列化
问题描述
我正在尝试结合 Jackson 的一些功能,以便我可以将{type,value}
json 中的一对反序列化为 java 中的 Union 类型,表示所有信息,但无法弄清楚如何去做。帮助将不胜感激。
这是我正在使用的内容:
- java Union 类型强制在任何时候只能设置一个值,将其视为枚举,但具有动态数据。
- 联合的值可以采用任意数量的类型,一些标量和一些对象或集合。
- 联合的多个选项可以共享相同的类型,即
noyes
并且在下面的示例中offon
都是boolean
类型。 - 我无法以任何方式控制 json 结构(它是传入/传出外部 API 的数据),因此根本无法更改它。
- Java 类是从 Thrift idl 生成的代码,因此我无法向它们添加注释或大幅调整它们的结构。虽然我控制了 idl,但希望保持它相当干净并且没有泄漏模式,例如
{type, value}
json 所需的泄漏模式,但不是强类型语言。 - json 中使用的类型名称(以及一些字段名称)与 java(和其他语言)关键字冲突,这就是每个值都带有后缀的原因:
noyes -> noYesValue
,float -> floatValue
一个具体的例子
我有一个看起来像这样的 json 文档:
{
"obj": [
{
"type": "noyes",
"value": true",
"id": 1
}, {
"type": "offon",
"value": false,
"id": 2
}, {
"type": "text",
"value": "hello",
"id": 3
}, {
"type": "float",
"value": 1.2,
"id": 4
}, {
"type": "times",
"value": [{"s": "12:22", "e": "16:00"}]
"id": 5
}
]
}
看起来像这样的java类:
class Response {
List<Item> obj;
}
class Item {
int id;
Value value;
}
class Value {
enum Fields { NO_YES_VALUE, OFF_ON_VALUE, TEXT_VALUE, FLOAT_VALUE, TIMES_VALUE }
static Item noYesValue(boolean noYes) {...}
static Item offOnValue(boolean offOn) {...}
static Item textValue(String text) {...}
static Item floatValue(float value) {...}
static Item timesValue(List<TimesValue> times) {...}
Fields setField;
Object fieldValue;
Value() {}
Value(Fields field, Object value) {setFieldValue(field, value);}
Fields getSetField() { return setField; }
Object getFieldValue() { return fieldValue; }
void setFieldValue(Fields field, Object value) {
// checkType(field, value);
this.setField = field;
this.fieldValue = value;
}
// these do have checks for the set field, types, null, etc
boolean getNoYesValue() { return (Boolean) fieldValue; }
void setNoYesValue(boolean v) { setField = NO_YES_VALUE; fieldValue = v; }
boolean getOffOnValue() { return (Boolean) fieldValue; }
void setOffOnValue(boolean v) { setField = OFF_ON_VALUE; fieldValue = v; }
// ...
}
class TimesValue {
String startTime;
String endTime;
}
对于 java 类,以下是等价的:
Value.noYesValue(false);
new Value().setNoYesValue(false);
new Value().setFieldValue(NO_YES_VALUE, false);
new Value(NO_YES_VALUE, false);
同样,以下是等价的:
value.getNoYesValue();
(Boolean) value.getFieldValue();
部分工作代码(做我想要的,但只适用于简单类型)
经过更多的挖掘和反复试验,我设法比以前走得更远。
首先,我把它Value
弄平Item
interface ItemMixin {
@JsonUnwrapped Value getValue();
}
这提升了我所有的Value
属性,因此它们成为Item
类的一部分,相当于{"value": {"type": "offon", "value": false}}
成为{"type": "offon", "value": false}
接下来我需要处理那个Fields
枚举,为此我编写了一个看起来像这样的自定义反序列化器并将它注册到一个模块
new StdDeserializer<Value.Fields>(Value.Fields.class) {
@Override
public Value.Fields deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String type = p.getText();
switch (type) {
case "noyes": return Value.Fields.NO_YES_VALUE;
case "offon": return Value.Fields.OFF_ON_VALUE;
case "float": return Value.Fields.FLOAT_VALUE;
case "text": return Value.Fields.TEXT_VALUE;
case "times": return Value.Fields.TIMES_VALUE;
// ...
}
ctxt.handleWeirdStringValue(Value.Fields.class, type, "Unsupported Value type");
return null;
}
}
最后我告诉Value
类型使用构造函数来创建对象
static abstract class ValueMixin {
@JsonCreator
ValueMixin(@JsonProperty("type") Value.Fields type, @JsonProperty("value") Object value) {}
}
这给了我所需的 90%,但是我现在遇到的问题是这仅适用于原始类型(boolean
,String
等),我的times
字段只是在我的代码中被反序列化为String
导致异常。
我尝试在参数上使用@JsonTypeInfo
和,为该字段创建一个属性并将注释放在那里,但无法让创建者正确解析所需的类型。@JsonSubTypes
value
解决方案
推荐阅读
- javascript - Why href link is not adding to the html using jquery
- java - JavaFX:打开新窗格时返回 NullPointerException
- linux - 查找指定目录下所有依赖于指定库的可执行文件
- c# - 带有 Blazor Web 应用程序的 TCP 接收器服务
- javascript - 如何修复我的代码以便我可以使用来自 componentDidMount (React) 的数据
- c++ - C程序中的std::map
- go - 在 Golang 中没有表单、URL 或查询参数的 2 个处理函数之间传递数据?
- c++ - 如何在我的自定义数据结构中实现 begin() 成员函数?
- r - 我将如何循环我的代码以获得 4 个直方图而不是 1 个?
- html - Bootstrap 折叠导航栏并跳转到页面的特定部分