兄弟对象的联合对象的Jackson反序列化

时间:2018-07-24 13:05:04

标签: java json jackson

我正在尝试结合Jackson的一些功能,以便可以将json中的{type,value}对反序列化为Java中表示所有信息的Union类型,但无法解决该问题。帮助将不胜感激。

这就是我正在使用的东西:

  1. java Union类型强制在任何时候都只能设置一个值,可以将其视为枚举,但要包含动态数据。
  2. 联合的值可以采用任何类型,一些标量和某些对象或集合。
  3. 联合的多个选项可以共享同一类型,即在下面的示例中,noyesoffon都是boolean类型。
  4. 我无论如何都无法控制json结构(这是传入/传出外部API的数据),因此根本无法更改。
  5. java类是从Thrift idl生成的代码,因此我无法向其添加批注或大幅调整其结构。虽然我可以控制idl,但是希望保持它的整洁,并且没有泄漏模式,例如json所需的{type, value},但不是强类型语言。
  6. json中使用的类型名称(和某些字段名称)与java(和其他语言)关键字冲突,这就是为什么每个值都带有后缀的原因:noyes -> noYesValuefloat -> 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%的需求,但是我现在遇到的问题是,它仅适用于基本类型(booleanString等),我的times字段只是反序列化为String导致我的代码出现异常。

我尝试在@JsonTypeInfo参数上使用@JsonSubTypesvalue,为该字段创建一个属性,然后在其中放置注释,但无法使创建者正确解析类型。

0 个答案:

没有答案