如何配置Jackson ObjectMapper将Foo类型的所有字段反序列化为SubclassOfFoo的实例?

时间:2021-07-31 18:02:15

I'm deserializing a large json value. Deeply nested within that value is a json object like the following:

我正在反序列化一个大的json值。深深嵌套在该值中的是一个json对象,如下所示:

{
  "fieldOne": "valueOne",
  "fieldTwo": {
    "innerField": "innerValue"
  }
}

I'm using the Jackson ObjectMapper to deserialize the large json value into a 3rd party class. Deeply nested within that 3rd party class is another 3rd party class:

我正在使用Jackson ObjectMapper将大的json值反序列化为第三方类。深深嵌套在第三方课程中的是另一个第三方课程:

public class DeepThirdPartyClass {
    public String fieldOne;
}

which unfortunately is missing the fieldTwo property. I can create my own class which adds the missing field:

不幸的是,它缺少fieldTwo属性。我可以创建自己的类,添加缺少的字段:

public class MyClass extends DeepThirdPartyClass {
    public MySubObject fieldTwo;
}

How do I configure jackson so that whenever it attempts to deserialize a value to DeepThirdPartyClass, it deserializes to MyClass instead?

如何配置jackson,以便每当它尝试将值反序列化为DeepThirdPartyClass时,它反序列化为MyClass?

2 个解决方案

#1


1  

I had similar requirement when I have to filter any not allowed characters in all String values. To create Object Mapper:

当我必须过滤所有String值中的任何不允许的字符时,我有类似的要求。要创建Object Mapper:

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        super();

        SimpleModule module = new SimpleModule("HTML XSS Serializer", new Version(1, 0, 0, "FINAL", "com.crowdoptic", "web"));
        module.addSerializer(String.class, new JsonHtmlXssSerializer());
        module.addDeserializer(String.class, new JsonHtmlXssDeserializer());

        this.registerModule(module);
    }
}

public class JsonHtmlXssDeserializer extends StdScalarDeserializer<String> {
    private static final Logger LOG = LogManager.getLogger(JsonHtmlXssDeserializer.class);

    public JsonHtmlXssDeserializer() { super(String.class); }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = StringDeserializer.instance.deserialize(p, ctxt);
        LOG.trace("in deserialize for value: " + value);
        String encodedValue = StringEscapeUtils.escapeHtml4(value);
        return encodedValue;
    }
    @Override
    public String deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
        return StringDeserializer.instance.deserializeWithType(jp, ctxt, typeDeserializer);
    }

    @Override
    public boolean isCachable() { return StringDeserializer.instance.isCachable(); }
}

In your case you can register your class deserializer, call super method of the object deserializer. Then instead of returning DeepThirdPartyClass, create object of MyClass, set field one from DeepThirdPartyClass and add second field. See StringDeserializer and others for implementation details and available properties.

在您的情况下,您可以注册您的类反序列化器,调用对象反序列化器的超级方法。然后,不是返回DeepThirdPartyClass,而是创建MyClass的对象,从DeepThirdPartyClass设置字段1并添加第二个字段。有关实现详细信息和可用属性,请参阅StringDeserializer和其他人。

Let me know if that helps.

如果有帮助,请告诉我。

#2


0  

I reworked @olga-khylkouskaya's solution to fit my problem:

我重写了@ olga-khylkouskaya的解决方案以适应我的问题:

@Test
public void newDeserializer() throws Exception {
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule module = new SimpleModule("DeepThirdPartyClass subclass override", new Version(1, 0, 0, "FINAL", "com.example", "deep-third-party-class-override"));
    module.addDeserializer(DeepThirdPartyClass.class, new JsonDeserializer<DeepThirdPartyClass>() {
        @Override
        public DeepThirdPartyClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return p.readValueAs(MyClass.class);
        }
    });
    objectMapper.registerModule(module);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    String json = "{\n" +
            "  \"middle\": {\n" +
            "    \"fieldOne\": \"valueOne\",\n" +
            "    \"fieldTwo\": {\n" +
            "      \"fieldThree\": \"valueThree\"\n" +
            "    }\n" +
            "  }\n" +
            "}\n";

    ThirdPartyClass thirdPartyClass = objectMapper.readValue(json, ThirdPartyClass.class);
}

public class ThirdPartyClass {
    public DeepThirdPartyClass middle;
}

public class InnerClass {
    public String fieldThree;
}

#1


1  

I had similar requirement when I have to filter any not allowed characters in all String values. To create Object Mapper:

当我必须过滤所有String值中的任何不允许的字符时,我有类似的要求。要创建Object Mapper:

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        super();

        SimpleModule module = new SimpleModule("HTML XSS Serializer", new Version(1, 0, 0, "FINAL", "com.crowdoptic", "web"));
        module.addSerializer(String.class, new JsonHtmlXssSerializer());
        module.addDeserializer(String.class, new JsonHtmlXssDeserializer());

        this.registerModule(module);
    }
}

public class JsonHtmlXssDeserializer extends StdScalarDeserializer<String> {
    private static final Logger LOG = LogManager.getLogger(JsonHtmlXssDeserializer.class);

    public JsonHtmlXssDeserializer() { super(String.class); }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = StringDeserializer.instance.deserialize(p, ctxt);
        LOG.trace("in deserialize for value: " + value);
        String encodedValue = StringEscapeUtils.escapeHtml4(value);
        return encodedValue;
    }
    @Override
    public String deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
        return StringDeserializer.instance.deserializeWithType(jp, ctxt, typeDeserializer);
    }

    @Override
    public boolean isCachable() { return StringDeserializer.instance.isCachable(); }
}

In your case you can register your class deserializer, call super method of the object deserializer. Then instead of returning DeepThirdPartyClass, create object of MyClass, set field one from DeepThirdPartyClass and add second field. See StringDeserializer and others for implementation details and available properties.

在您的情况下,您可以注册您的类反序列化器,调用对象反序列化器的超级方法。然后,不是返回DeepThirdPartyClass,而是创建MyClass的对象,从DeepThirdPartyClass设置字段1并添加第二个字段。有关实现详细信息和可用属性,请参阅StringDeserializer和其他人。

Let me know if that helps.

如果有帮助,请告诉我。

#2


0  

I reworked @olga-khylkouskaya's solution to fit my problem:

我重写了@ olga-khylkouskaya的解决方案以适应我的问题:

@Test
public void newDeserializer() throws Exception {
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule module = new SimpleModule("DeepThirdPartyClass subclass override", new Version(1, 0, 0, "FINAL", "com.example", "deep-third-party-class-override"));
    module.addDeserializer(DeepThirdPartyClass.class, new JsonDeserializer<DeepThirdPartyClass>() {
        @Override
        public DeepThirdPartyClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return p.readValueAs(MyClass.class);
        }
    });
    objectMapper.registerModule(module);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    String json = "{\n" +
            "  \"middle\": {\n" +
            "    \"fieldOne\": \"valueOne\",\n" +
            "    \"fieldTwo\": {\n" +
            "      \"fieldThree\": \"valueThree\"\n" +
            "    }\n" +
            "  }\n" +
            "}\n";

    ThirdPartyClass thirdPartyClass = objectMapper.readValue(json, ThirdPartyClass.class);
}

public class ThirdPartyClass {
    public DeepThirdPartyClass middle;
}

public class InnerClass {
    public String fieldThree;
}