用杰克逊反序列化字符串化的(带引号的)嵌套对象 [英] Deserializing stringified (quote enclosed) nested objects with Jackson

查看:94
本文介绍了用杰克逊反序列化字符串化的(带引号的)嵌套对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用"RESTful"服务(通过RestTemplate),该服务生成JSON如下:

I am consuming a "RESTful" service (via RestTemplate) that produces JSON as follows:

{
    "id": "abcd1234",
    "name": "test",
    "connections": {
        "default": "http://foo.com/api/",
        "dev": "http://dev.foo.com/api/v2"
    },
    "settings": {
        "foo": "{\n \"fooId\": 1, \"token\": \"abc\"}",
        "bar": "{\"barId\": 2, \"accountId\": \"d7cj3\"}"
    }
}

请注意settings.foosettings.bar值,这会导致反序列化问题.我希望能够反序列化为对象(例如settings.getFoo().getFooId()settings.getFoo().getToken()).

Note the settings.foo and settings.bar values, which cause issues on deserialization. I would like to be able to deserialize into objects (e.g., settings.getFoo().getFooId(), settings.getFoo().getToken()).

我能够使用自定义反序列化器专门针对Foo的实例解决此问题:

I was able to solve this specifically for an instance of Foo with a custom deserializer:

public class FooDeserializer extends JsonDeserializer<Foo> {
    @Override
    public Foo deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
        JsonNode node = jp.getCodec().readTree(jp);

        String text = node.toString();
        String trimmed = text.substring(1, text.length() - 1);
        trimmed = trimmed.replace("\\", "");
        trimmed = trimmed.replace("\n", "");

        ObjectMapper mapper = new ObjectMapper();
        JsonNode obj = mapper.readTree(trimmed);
        Foo result = mapper.convertValue(obj, Foo.class);

        return result;
    }
}

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Settings {
    @JsonDeserialize(using = FooDeserializer.class)
    private Foo foo;

    private Bar bar;
}

但是,现在,如果要反序列化settings.bar,则需要实现另一个自定义反序列化器.因此,我实现了一个通用的反序列化器,如下所示:

However, now if I want to deserialize settings.bar, I need to implement another custom deserializer. So I implemented a generic deserializer as follows:

public class QuotedObjectDeserializer<T> extends JsonDeserializer<T> implements ContextualDeserializer {
    private Class<?> targetType;
    private ObjectMapper mapper;

    public QuotedObjectDeserializer(Class<?> targetType, ObjectMapper mapper) {
        this.targetType = targetType;
        this.mapper = mapper;
    }

    @Override
    public JsonDeserializer<T> createContextual(DeserializationContext context, BeanProperty property) {
        this.targetType = property.getType().containedType(1).getRawClass();
        return new QuotedObjectDeserializer<T>(this.targetType, this.mapper);
    }

    @Override
    public T deserialize(JsonParser jp, DeserializationContext context) throws IOException {
        JsonNode node = jp.getCodec().readTree(jp);
        String text = node.toString();
        String trimmed = text.substring(1, text.length() - 1);
        trimmed = trimmed.replace("\\", "");
        trimmed = trimmed.replace("\n", "");

        JsonNode obj = this.mapper.readTree(trimmed);
        return this.mapper.convertValue(obj, this.mapper.getTypeFactory().constructType(this.targetType));
    }
}

现在,我不确定如何真正使用解串器,因为用@JsonDeserialize(using = QuotedObjectDeserializer.class)注释Settings.Foo显然是行不通的.

Now I'm not sure how to actually use the deserializer, as annotating Settings.Foo with @JsonDeserialize(using = QuotedObjectDeserializer.class) obviously does not work.

有没有一种方法可以注释属性以使用通用的自定义反序列化器?或者,也许更有可能,是否有一种方法可以配置默认的反序列化器,以处理示例JSON中返回的 stringy 对象?

Is there a way to annotate properties to use a generic custom deserializer? Or, perhaps more likely, is there a way to configure the default deserializers to handle the stringy objects returned in my example JSON?

编辑:这里的问题是将settings.foosettings.bar反序列化为对象. JSON表示将这些对象包装在引号中(并用转义序列污染),因此将它们反序列化为Strings.

Edit: The problem here is specifically deserializing settings.foo and settings.bar as objects. The JSON representation has these objects wrapped in quotes (and polluted with escape sequences), so they are deserialized as Strings.

推荐答案

很抱歉,此处的代码长度.这里有很多快捷方式(没有封装;在defaulte中添加了e以避免关键字等),但意图是存在的 型号类别:

Sorry about the length of the code here. There are plenty of shortcuts here (no encapsulation; added e to defaulte to avoid keyword etc.) but the intent is there Model class:

package com.odwyer.rian.test;

import java.io.IOException;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Model {
    public String id;
    public String name;
    public Connections connections;
    public Settings settings;

    public static class Connections {
        public String defaulte;
        public String dev;

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    public static class Foo {
        public Foo () {}

        @JsonCreator
        public static Foo create(String str) throws JsonParseException, JsonMappingException, IOException {
            return (new ObjectMapper()).readValue(str, Foo.class);
        }

        public Integer fooId;
        public String token;

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    public static class Bar {
        public Bar() {}

        @JsonCreator
        public static Bar create(String str) throws JsonParseException, JsonMappingException, IOException {
            return (new ObjectMapper()).readValue(str, Bar.class);
        }

        public Integer barId;
        public String accountId;

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    public static class Settings {
        public Foo foo;
        public Bar bar;

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this);
    }
}

呼叫者:

package com.odwyer.rian.test;

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TestClass {
    private static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        Scanner file = new Scanner(new File("test.json"));
        String jsonStr = file.useDelimiter("\\Z").next();

        Model model = objectMapper.readValue(jsonStr, Model.class);

        System.out.println(model.toString());
    }
}

结果(格式化起来太麻烦了,但是就这么简单!): com.odwyer.rian.test.Model@190083e [id = abcd1234,name = test,connections = com.odwyer.rian.test.Model $ Connections @ 170d1f3f [defaulte =

The result (too much hassle to format out but it is all there!): com.odwyer.rian.test.Model@190083e[id=abcd1234,name=test,connections=com.odwyer.rian.test.Model$Connections@170d1f3f[defaulte=http://foo.com/api/,dev=http://dev.foo.com/api/v2],settings=com.odwyer.rian.test.Model$Settings@5e7e6ceb[foo=com.odwyer.rian.test.Model$Foo@3e20e8c4[fooId=1,token=abc],bar=com.odwyer.rian.test.Model$Bar@6291bbb9[barId=2,accountId=d7cj3]]]

该键由特德(Ted)和他的帖子提供( https://stackoverflow.com/a/8369322/2960707 )是@JsonCreator批注

The key, courtesy of Ted and his post (https://stackoverflow.com/a/8369322/2960707) is the @JsonCreator annotation

这篇关于用杰克逊反序列化字符串化的(带引号的)嵌套对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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