【问题标题】:Providing Jackson Mapper multiple ways to deserialize the same object提供 Jackson Mapper 多种方法来反序列化同一个对象
【发布时间】:2014-03-14 11:38:36
【问题描述】:

我正在尝试反序列化两种类型的 json:

{
    name: "bob",
    worksAt: {
        name: "Bobs department store",
        location: "downtown"
    },
    age: 46
}

{
    name: "Tom",
    worksAt: "company:Bobs department store",
    age: 27
}

进入这些对象:

第一种方式创建两个新对象,第二种方式根据字符串的内容从数据库中请求对象。

有点像杰克逊映射器如何将任意字符串反序列化为对象,对于像这样的对象:

public class Company{
    public String name;
    public Employee[] employees
    public Company(){}
    public Company(String json){
        //turn string into object using whatever encoding you want blah blah blah...
    }
}

问题是我两者都需要。我需要它来处理对象和字符串。两者都可以从同一个输入到达。

我尝试的第一个想法是制作一个转换器

它说这些创建了一个委托类型以传递给反序列化器,但即使数据类型不是字符串,也始终应用转换器。所以这没有用。

我也尝试了一个普通的反序列化器,但我找不到一种方法来推迟到 BeanDeserializer。 beanDeserializer 太复杂了,我无法手动实例化它。我也看不出有办法推迟到杰克逊映射器中的默认反序列化器。

我是否必须重新实现杰克逊映射器反序列化才能做到这一点?反序列化器有没有办法说“我不能这样做,使用默认实现。”?

编辑:一些进一步的进展。根据 Jackson Mapper 源代码,您可以像这样实例化 bean 反序列化器:

DeserializationConfig config = ctxt.getConfig();
JavaType type = config.constructType(_valueClass);
BeanDescription introspect = config.introspect(type);
JsonDeserializer<Object> beanDeserializer = ctxt.getFactory().createBeanDeserializer(ctxt, type , introspect);

但由于某种原因,所有 _beanProperties 都为其 _valueDeserializer 设置了 FailingDeserializer 并且整个事情都失败了。所以我不知道为什么会这样......

【问题讨论】:

    标签: jackson


    【解决方案1】:

    你试过writing a custom deserializer吗?这使您可以最大程度地控制 Jackson 如何反序列化对象。您也许可以尝试以一种方式反序列化,如果出现错误,请尝试另一种方式。

    Jackson 也可以处理 polymorphic deserialization,尽管这需要对 json 进行一些小的更改以包含类型信息,而且听起来您的问题约束可能不允许这样做。

    【讨论】:

    • 是的。事实上,我确实尝试实现自定义反序列化器。然而我碰壁了。它允许我从字符串中反序列化一个对象,但是如果传入一个 json 对象会发生什么?在我的系统中手动反序列化许多嵌套对象是站不住脚的。我需要使用 bean 反序列化器来做到这一点。但是,一旦我为对象定义了反序列化器,我就无法再访问 bean 反序列化器了!
    【解决方案2】:

    如果我正确理解问题,我会推荐使用 JsonNode。您可以像这样在顶级类型中定义 setter:

    setWorksAt(JsonNode node) {
        if (node.getNodeType == JsonNodeType.STRING) {
            String name = node.getText();
            name = name.substring(name.lastIndexOf(':'));
            this.company = new Company(name);
        } else if (node.getNodeType == JsonNodeType.OBJECT) {
            this.company = mapper.treeToValue(node, Company.class);
        }
    }
    

    这允许您处理两个单独的 worksFor 输入,同时仍然允许标准映射器处理 OBJECT 案例的任何子结构。

    【讨论】:

    • 我不知道我可以在 setter 中获得 JsonNode!但是,我如何获得映射器?我正在创建的对象是 POJO,只不过是结构,并且无法访问映射器。我不喜欢使用singelton 将其放入其中的想法。此外,还有一个缺点是这与对象中的设置器有关,这也可以防止我在没有自动装配更多东西的情况下弄乱数据库。我必须在我的项目中违反一些标准才能摆脱这个问题。公平起见。我不认为我在问题中提到了这些限制......
    • 是的,我在我的实现中使用单例来获取映射器。我认为您也可以使用依赖注入,但我没有使用过,所以我不知道具体细节。如果您能解释您的数据库限制,我可以尝试考虑是否有办法使用 Jackson 来适应两者。
    【解决方案3】:

    使用最新版本的 Jackson(我认为是 2.8+,肯定适用于 2.9),您可以使用多个 @JsonCreator 并执行以下操作:

    public class Company {
        private String name;
        private String location;
    
        private Company(String name, String location) {
            this.name = name;
            this.location = location;
        }
        private Company(String stringRepresentation) {
           // add code here to parse string and extract name and location
        }
    
        @JsonCreator
        private static Company fromJson(
                @JsonProperty("name") String name,
                @JsonProperty("location") String location)
        {
            return new Company(name, location);
        }
    
        @JsonCreator
        private static Company fromJson(String str) {
            return Company(str);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-01-01
      • 2015-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-17
      • 1970-01-01
      • 2013-02-05
      • 2020-08-07
      相关资源
      最近更新 更多