在Java中

时间:2015-12-22 00:46:44

标签: java json jackson gson deserialization

我正在为一个有点复杂的API构建一个客户端库。但是,不同的响应对象之间存在显着的共性,如下所示:

{
    "response": "Success",
    "delay": 0.241,
    "time": 125425234,
    "message": null,
    "a": "Payloads"
}

{
    "response": "AuthFailure",
    "delay": 0.112,
    "time": 1324515123,
    "message": "Wrong password",
    "b": 1234
}

{
    "response": "Success",
    "delay": 0.294,
    "time": 12461246123,
    "message": null,
    "c": True
    "d": 245.1
}

我想要分解公共部分,并希望将它们反序列化为对象组合:

Response<AData>
Response<BData>
Response<CDData>

(类定义看起来像):

class Response<T> {
    final Response response;
    final Double delay;
    final Timestamp time;
    final String message;
    final T inner;
    ...
}

class AData {
    final String a;
    ...
}

class BData {
    final int b;
    ...
}

class BData {
    final bool c;
    final double d;
    ...
}

这很像“JsonUnwrapped”杰克逊注释的倒数。继承也会奏效。

不幸的是,我无法找到一种方法在Jackson中以合理的方式完成这一操作,而这种方式与ObjectMapper系统的其余部分相结合,而无需编写重要的附加模块。我错过了什么吗?是否有更好的方法来做这类事情?

2 个答案:

答案 0 :(得分:1)

这里的问题是你(或杰​​克逊)需要知道需要用什么对象来转换请求。有两种方法:

1)使用继承。由于Jackson会为您处理所有事情,这种方法更加强大,但这种方法需要添加一个标记,杰克逊将使用该标记来选择应该用于转换的对象类型。不确定您是否可以添加这些标记,但下面的代码可以让您了解如何完成这些标记。 它非常简单 - 你只需要添加@JsonTypeInfo来配置哪个字段将用作标记,@ jsonSubTypes来定义所有可用于转换响应的类。

class ResponseA extends BaseResponse {
    private String a;
}

class ResponseB extends BaseResponse {
    private String b;
}

class ResponseCD extends BaseResponse {
    private boolean c;
    private double d;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = ResponseA.class, name = "a"),
        @JsonSubTypes.Type(value = ResponseB.class, name = "b"),
        @JsonSubTypes.Type(value = ResponseCD.class, name = "cd")
})
class BaseResponse {
    private String response;
    private double delay;
    private long time;
    private String message;
}

public class DynamicResponseInheritance {
    private static final String RESPONSE_A = "{\n" +
            "    \"type\": \"a\",\n" +
            "    \"response\": \"Success\",\n" +
            "    \"delay\": 0.241,\n" +
            "    \"time\": 125425234,\n" +
            "    \"message\": null,\n" +
            "    \"a\": \"Payloads\"\n" +
            "}";

    private static final String RESPONSE_B = "{\n" +
            "    \"type\": \"b\",\n" +
            "    \"response\": \"AuthFailure\",\n" +
            "    \"delay\": 0.112,\n" +
            "    \"time\": 1324515123,\n" +
            "    \"message\": \"Wrong password\",\n" +
            "    \"b\": 1234\n" +
            "}";

    private static final String RESPONSE_CD = "{\n" +
            "    \"type\": \"cd\",\n" +
            "    \"response\": \"Success\",\n" +
            "    \"delay\": 0.294,\n" +
            "    \"time\": 12461246123,\n" +
            "    \"message\": null,\n" +
            "    \"c\": true,\n" +
            "    \"d\": 245.1\n" +
            "}";

    public static void main(String []args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();

        BaseResponse responseA = objectMapper.readValue(RESPONSE_A, BaseResponse.class);
        BaseResponse responseB = objectMapper.readValue(RESPONSE_B, BaseResponse.class);
        BaseResponse responseCD = objectMapper.readValue(RESPONSE_CD, BaseResponse.class);

        System.out.println(responseA);
        System.out.println(responseB);
        System.out.println(responseCD);
    }
}

2)实施自定义反序列化程序。它也很简单,但在这种情况下,如果需要添加新类,则需要更新反序列化器。这种方法的好处是您不需要修改响应。

class ResponseDeserializer extends JsonDeserializer<BaseResponse> {
    @Override
    public BaseResponse deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        ObjectMapper mapper = (ObjectMapper) parser.getCodec();
        JsonNode root = parser.getCodec().readTree(parser);
        JsonNode a = root.get("a");
        if (a != null) {
            String content = root.toString();
            return mapper.readValue(content, ResponseA.class);
        }
        JsonNode b = root.get("b");
        if (b != null) {
            String content = root.toString();
            return mapper.readValue(content, ResponseB.class);
        }
        JsonNode c = root.get("c");
        if (c != null) {
            String content = root.toString();
            return mapper.readValue(content, ResponseCD.class);
        }
        return null;
    }
}

class ResponseA extends BaseResponse {
    private String a;
}

class ResponseB extends BaseResponse {
    private String b;
}

class ResponseCD extends BaseResponse {
    private boolean c;
    private double d;
}

class BaseResponse {
    private String response;
    private double delay;
    private long time;
    private String message;
}

public class DynamicResponseCustomDeserializer {
    private static final String RESPONSE_A = "{\n" +
            "    \"response\": \"Success\",\n" +
            "    \"delay\": 0.241,\n" +
            "    \"time\": 125425234,\n" +
            "    \"message\": null,\n" +
            "    \"a\": \"Payloads\"\n" +
            "}";

    private static final String RESPONSE_B = "{\n" +
            "    \"response\": \"AuthFailure\",\n" +
            "    \"delay\": 0.112,\n" +
            "    \"time\": 1324515123,\n" +
            "    \"message\": \"Wrong password\",\n" +
            "    \"b\": 1234\n" +
            "}";

    private static final String RESPONSE_CD = "{\n" +
            "    \"response\": \"Success\",\n" +
            "    \"delay\": 0.294,\n" +
            "    \"time\": 12461246123,\n" +
            "    \"message\": null,\n" +
            "    \"c\": true,\n" +
            "    \"d\": 245.1\n" +
            "}";

    public static void main(String []args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(BaseResponse.class, new ResponseDeserializer());
        objectMapper.registerModule(module);


        BaseResponse responseA = objectMapper.readValue(RESPONSE_A, BaseResponse.class);
        BaseResponse responseB = objectMapper.readValue(RESPONSE_B, BaseResponse.class);
        BaseResponse responseCD = objectMapper.readValue(RESPONSE_CD, BaseResponse.class);

        System.out.println(responseA);
        System.out.println(responseB);
        System.out.println(responseCD);
    }
}

答案 1 :(得分:0)

这不是@JsonUnwrapped的倒数。这个 IS @JsonUnwrapped,因为json指定了Response内部字段内的变量。

你想要的是告诉杰克逊你的数据的通用结构是什么,你可以用杰克逊的内部类型系统来实现这个目标:

这是我使用的类定义:

public class Response<T> {
    public String response;
    public Double delay;
    public Timestamp time;
    public String message;
    @JsonUnwrapped
    public T inner;
}

public class AData {
    public String a;
}

public class BData {
    public int b;
}

public class CData {
    public boolean c;
    public double d;
}

这就是你告诉杰克逊的通用结构:

    ObjectMapper mapper = new ObjectMapper();
    TypeFactory f = mapper.getTypeFactory();
    JavaType responseAData = f.constructParametrizedType(Response.class, Response.class, AData.class);

然后反序列化可以通常的方式进行:

    try (InputStream is = new FileInputStream("C://Temp/xx.json")) {
        Response<AData> r = (Response<AData>) mapper.readValue(is, responseAData);
        System.out.println(r.inner.a );
    } catch (Exception e) {
        e.printStackTrace();
    }

根据问题指定输入,我输出为Payloads
这里的狡猾警告是,您的客户需要事先知道响应的类型