【问题标题】:Custom Json Deserializer for a generic class type通用类类型的自定义 Json 反序列化器
【发布时间】:2018-01-25 07:01:24
【问题描述】:

考虑一个“服装”类型的 json:

{
  "id":"123",
  "version":2,
  "apparel":{
    "category":[
      {
        "id":"a1",
        "style":"top",
        "comments":[
         { 
          "header":{
            "type":"apparel.detail.Summary",
            "major_version":1,
            "minor_version":0
          },
          "summary": "notes"
        }]
      }
    ]
  },
  "accessories":[
    {
      "header":{
        "type":"accessories.detail.Handbag",
        "major_version":1,
        "minor_version":0
      },
      "details":{
        "brand":"Gucci",
        "sno.":"G12"
      },
      "color":"Red",
    },
    {
      "header":{
        "type":"accessories.detail.Hat",
        "major_version":1,
        "minor_version":0
      },
      "details":{
        "brand":"Adidas",
        "sno.":"A12"
      }
    }
  ]
}

我无法访问“服装”,我无法添加任何字段级别或类级别的 json 注释。 json 中有一个属性“header”可以帮助我确定我想要将该实体转换为的类的类型。一旦确定了类类型,我将从我的 json 中删除标头(因为标头未在我的目标类类型中定义,因为反序列化将失败)

我需要编写一个返回通用类类型对象的自定义反序列化器。它将检查是否有header,获取目标类名,删除header并将其反序列化为获取的目标类并返回。

这是我编写的代码,但它不起作用,我什至不确定是否可以在 SimpleModule 中注入具有通用返回类型的自定义反序列化器。

@Singleton
@Provides
private Transformer provideTransformer(final HeaderDeserializer headerDeserializer) {
    final SimpleModule simpleModule = new SimpleModule();
    simpleModule.addDeserializer(Object.class, headerDeserializer);
    mapper.registerModule(simpleModule);
}


@Singleton
@Provides
private HeaderDeserializer provideHeaderDeserializer(final ObjectMapper objectMapper) {
    return new HeaderDeserializer(objectMapper);
}

@Singleton
@Provides
private ObjectMapper provideObjectMapper() {
    final ObjectMapper mapper = new ObjectMapper()
            // Tell object mapper how to handle joda-time.
            .registerModule(new JodaModule())
            // include non-null values only
            .setSerializationInclusion(Include.NON_NULL)
            // ensures that timezone is preserved
    .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
    return mapper;
}

我的 HeaderDeserializer 看起来像这样:

public class HeaderDeserializer<T> extends StdDeserializer<T> {

private static final long serialVersionUID = 1L;

private final ObjectMapper mapper;

public HeaderDeserializer(final ObjectMapper mapper) {
    this(null, mapper);
}

public HeaderDeserializer(final Class<?> vc, final ObjectMapper mapper) {
    super(vc);
    this.mapper = mapper;
}

@Override
public T deserialize(final JsonParser jp, final DeserializationContext ctx) {
    Object value = null;
    try {
        JsonNode node = this.mapper.readTree(jp);
        JsonNode header = node.get("header");
        if (node.has("header")) {
            String targetClass = header.get("type").textValue();
            removeHeaderFromJsonDoc(node);
            value = this.mapper.readValue(jp, Class.forName(targetClass));
        }
    } catch (final IOException e) {
        throw new UncheckedIOException(e);
    } catch (final ClassNotFoundException e) {
        // do somehting
    }
    return (T) value;
}

private void removeHeaderFromJsonDoc(final JsonNode document) {
    final Iterator<Entry<String, JsonNode>> itr = document.fields();
    while (itr.hasNext()) {
        final Entry<String, JsonNode> childNodeEntry = itr.next();
        if (childNodeEntry.getKey().equals("header")) {
            itr.remove();
        }
    }
  }
}

我的主要反序列化器将使用上面定义的自定义反序列化器,如下所示:

public final Clothing deserialize(
        final String stringValue,
        final Class<? extends Clothing> clazz) {
    try {
        return this.objectMapper.readValue(stringValue, clazz);
    } catch (final IOException e) {
        throw new IllegalArgumentException();
    }
}

【问题讨论】:

  • 什么“不起作用”?你有例外吗?对象类型错误?
  • HeaderDeserializer 是用泛型定义的,但被实例化为原始类型并关联到 Object.class ,那么为什么用泛型定义它呢?让它返回Object
  • 反序列化时不调用HeaderDeserializer的deserialize方法。我也尝试了你的建议,但都没有进入反序列化方法。

标签: json generics jackson deserialization json-deserialization


【解决方案1】:

this.objectMapper.readValue(stringValue, clazz); 此 readValue 方法中 'clazz' 的类类型应与 simpleModule.addDeserializer 中传递的类类型匹配。


它不会进入您的反序列化器,因为您正在将反序列化器添加到 SimpleModule 的“对象”类并读取传递给“服装反序列化”的不同类的值,

【讨论】:

    猜你喜欢
    • 2015-07-07
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多