如何与杰克逊打鸭子? [英] How to do duck typing with Jackson?

查看:102
本文介绍了如何与杰克逊打鸭子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用Jackson实现某种鸭子打字。我已经在这里看到了示例6 http:// programmerbruce .blogspot.de / 2011/05 /反序列化-JSON与 - 杰克逊into.html 。但是这个不起作用,如果基类本身不是抽象的(导致无限循环)。
所以我的问题是:有没有办法实现某种回调,我可以在其中进行鸭子打字(检查JSON字符串中的相关属性)然后返回杰克逊应该用来进行反序列化的类型照常?

I'd like to implement some kind of duck typing using Jackson. I've already seen Example 6 here http://programmerbruce.blogspot.de/2011/05/deserialize-json-with-jackson-into.html. But this one does not work, if the base class itself is not abstract (results in an infinite loop). So my question is: Is there a way to implement some kind of callback, where I can do the duck typing (check for relevant attributes within the JSON String) and then return the type that Jackson should use to do the deserialization as usual?

这是一些示例代码。它适用于子类 ExtendedOptions 但不适用于基类选项 ...

Here's some example Code. It works for the subclass ExtendedOptions but not for the base class Options...

public class Options {

    private int size;

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public String toString() {
        return "Options";
    }
}

public class ExtendedOptions extends Options {

    private String feature;

    public String getFeature() {
        return feature;
    }
    public void setFeature(String feature) {
        this.feature = feature;
    }

    @Override
    public String toString() {
        return "ExtendedOptions";
    }

}

public class OptionsDeserializer extends
        StdDeserializer<Options> {

    OptionsDeserializer() {
        super(Options.class);
    }

    @Override
    public Options deserialize(JsonParser jp,
            DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        ObjectNode root = (ObjectNode) mapper.readTree(jp);
        Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();
        while (elementsIterator.hasNext()) {
            Entry<String, JsonNode> element = elementsIterator.next();
            String name = element.getKey();
            // has "feature"? => It's an ExtendedOptions object
            if ("feature".equals(name)) {
                return mapper.treeToValue(root, ExtendedOptions.class);
            }
        }
        // otherwise it's just an Options object
        return mapper.treeToValue(root, Options.class);
    }

}

public class JacksonTest {

    public static void main(String[] args) throws JsonParseException,
            JsonMappingException, Exception {
        String optionsString = "{ \"size\": 5 }";
        String extendedOptionsString = "{ \"size\": 5, \"feature\" : \"theFeature\" }";

        OptionsDeserializer deserializer = new OptionsDeserializer();
        @SuppressWarnings("deprecation")
        SimpleModule module = new SimpleModule(
                "PolymorphicDeserializerModule", new Version(1, 0, 0,
                        null));
        module.addDeserializer(Options.class, deserializer);

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);

        // works
        ExtendedOptions extendedOptions = (ExtendedOptions) mapper.readValue(extendedOptionsString, Options.class);
        System.out.println(extendedOptions);

        // results in infinite loop!!!
        Options options = mapper.readValue(optionsString, Options.class);
        System.out.println(options);
    }

}

编辑:

我已经尝试根据StaxMan的提示实现BeanDeserializerModifier。这是modifyDeserializer方法:

I've tried implementing a BeanDeserializerModifier according to StaxMan's hint. Here's the modifyDeserializer method:

@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {       
    List<BeanPropertyDefinition> properties = beanDesc.findProperties();
    for(BeanPropertyDefinition property: properties) {
        String name = property.getName();
        // has "feature"? => It's an ExtendedOptions object
        System.out.println(name);
        if("feature".equals(name)) {
            System.out.println("It's an extended object!");
            // should return special deserializer here...
            return deserializer;
        }
    }
    System.out.println("It's not an extended object!");
    return deserializer;
}

这不起作用,因为 beanDesc 包含信息仅限选项类。这意味着您没有获得有关当前json流的任何信息,因此您无法确定必须返回哪个Deserializer。
但是我找到了一个有效的解决方案,但它不是100%完美:

That did not work, because beanDesc contains information on Option class only. That means you don't get any information on the current json stream, so you can't decide which Deserializer must be returned. However I've found a solution that works, but it's not 100% perfect:

@Override
public Options deserialize(JsonParser jp,
        DeserializationContext ctxt) throws IOException,
        JsonProcessingException {

    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    ObjectNode root = (ObjectNode) mapper.readTree(jp);

    Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();
    while (elementsIterator.hasNext()) {
        Entry<String, JsonNode> element = elementsIterator.next();
        String name = element.getKey();
        // has "feature"? => It's an ExtendedOptions object
        if ("feature".equals(name)) {
            return mapper.treeToValue(root, ExtendedOptions.class);
        }
    }
    // otherwise it's just an Options object
    ObjectMapper origMapper = new ObjectMapper();       
    return origMapper.treeToValue(root, Options.class);
}

这里我只是创建一个新的ObjectMapper实例来反序列化基类型选项的。正如我所说,它有效。但是:

Here I simply create a new instance of ObjectMapper to deserialize the base type Options. As I said, it works. But:

1)创建一个新的ObjectMapper实例可能很昂贵(使用静态属性?)。

1) It might be expensive to create a new instance of ObjectMapper (use static attribute?).

2)此解决方案不适用于嵌套的polymorph对象。例如,如果Options将包含一个本身就是多态的属性。

2) This solution will not work with nested polymorph objects. For example, if Options would contain an attribute that is itself polymorph.

所以这是另一个问题:有没有办法取消注册反序列化器?如果是这样,我可以用这样的东西替换最后两行:

So here's another question: Is there a way to unregister a deserializer? If so I could replace the last 2 lines with something like this:

mapper.unregister(this);
Options result = mapper.treeToValue(root, Options.class);
mapper.register(this);
return result;

编辑2

好的,你是对的。取消注册不是一个好的解决方案。我没想到多线程;-)
无论如何,我试过这个:

Ok, you are right. Unregistering is not a good solution. I did not think of multi-threading ;-) Anyway, I tried this one:

@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
        BeanDescription beanDesc, JsonDeserializer<?> deserializer) {       
    return new OptionsDeserializer();
}

但这导致我进入同样的无限循环。

But that leads me to the same infinite-loop.

推荐答案

没有自动支持,虽然已经请求了这样的(隐式类型),请参阅Jira问题 JACKSON-500

There is no automatic support, although something like this (implicit type) has been requested, see Jira issue JACKSON-500.

可能会使用一种可能有用的可能性 BeanDeserializerModifier :你可以访问构建的标准 JsonDeserializer ,但是可以添加你的包装类型。这可以通过定义 modifyDeserializer()来完成,否则完成(默认) JsonDeserializer ;然后你可以构建自己的实例,传递反序列化器。

One existing possibility that might work would be used of BeanDeserializerModifier: you would get access to standard JsonDeserializer that is built, but could then add your wrapper sort of around it. This would be done by defining modifyDeserializer(), which is given otherwise complete (default) JsonDeserializer; and then you can just build your own instance, passing that deserializer.

这篇关于如何与杰克逊打鸭子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆