Jackson Json Unwrapped和generic

时间:2018-06-15 10:40:12

标签: java generics jackson

我有两个班级:

public class ResponseInfo {

    private final int code;

    private final String description;

    @JsonCreator
    public static ResponseInfo of(
            @JsonProperty("code") int code,
            @JsonProperty("description") String description
    ) {
        return new ResponseInfo(code, description);
    }

    private ResponseInfo(
            int code,
            String description
    ) {
        this.code = code;
        this.description = description;
    }

    @JsonProperty("code")
    public int code() {
        return code;
    }

    @JsonProperty("description")
    public String description() {
        return description;
    }
}

 public class Response<T> {

    private final ResponseInfo responseInfo;

    private final T payload;

    public static <T> Response<T> of(ResponseInfo responseInfo, T payload) {
        return new Response<>(responseInfo, payload);
    }

    private Response(ResponseInfo responseInfo, T payload) {
        this.responseInfo = responseInfo;
        this.payload = payload;
    }

    @JsonUnwrapped
    public ResponseInfo responseInfo() {
        return responseInfo;
    }

    @JsonUnwrapped
    public T payload() {
        return payload;
    }
}

我使用它们将其他信息添加到响应中(作为代码和描述)。例如:

Response.of(ResponseInfo.of(0, "OK"), User.of("Oleg", 23))

将序列化为:

{ "age": 23, "code": 0, "description": "OK", "name": "Oleg" }

如何进行响应的反序列化?

我无法直接在@JsonProperty中使用@JsonCreator因为我不知道有效负载的属性。 JsonCreator @JsonUnwrapped也不起作用。

我正在使用jackson-datatype-jdk8:2.9.5

1 个答案:

答案 0 :(得分:0)

我已经创建了实现。

public class ResponseDeserializer
        extends JsonDeserializer<Response<?>>
        implements ContextualDeserializer {

    private JavaType type;

    @SuppressWarnings("unused")
    public ResponseDeserializer() {
    }

    private ResponseDeserializer(JavaType type) {
        this.type = type;
    }

    @Override
    public JsonDeserializer<?> createContextual(
            DeserializationContext context,
            BeanProperty beanProperty
    ) {
        JavaType contextualType = context.getContextualType();

        if(contextualType == null) {
            contextualType = beanProperty.getMember()
                    .getType();
        }

        if (!contextualType.isTypeOrSubTypeOf(Response.class)) {
            throw new IllegalArgumentException("contextualType should be " + Response.class.getName());
        }

        final JavaType payloadType = contextualType.containedType(0);

        return new ResponseDeserializer(payloadType);
    }

    @Override
    public Response<?> deserialize(
            JsonParser jsonParser,
            DeserializationContext context
    ) throws IOException {
        final ObjectCodec codec = jsonParser.getCodec();
        JsonNode rootNode = codec.readTree(jsonParser);

        final ResponseInfo responseInfo = ResponseInfo.of(
                rootNode.get("code").asInt(),
                rootNode.get("description").asText()
        );

        final JsonNode payloadNode = createPayloadNode(rootNode, codec);
        final JsonParser payloadParser = payloadNode.traverse();

        final Object payload = codec.readValue(payloadParser, type);
        return Response.of(responseInfo, payload);
    }

    private JsonNode createPayloadNode(JsonNode rootNode, ObjectCodec codec) {
        final Map<String, JsonNode> remainingNodes = findRemainingNodes(rootNode);

        if(remainingNodes.size() == 1) {
            final JsonNode payloadNode = remainingNodes.get("payload");

            if(payloadNode != null && !payloadNode.isObject()) {
                return payloadNode;
            }
        }

        return buildRemainingNode(remainingNodes, codec);
    }

    private JsonNode buildRemainingNode(Map<String, JsonNode> remainingNodes, ObjectCodec codec) {
        final ObjectNode remainingNode = (ObjectNode) codec.createObjectNode();
        remainingNodes.forEach(remainingNode::set);
        return remainingNode;
    }

    private Map<String, JsonNode> findRemainingNodes(JsonNode rootNode) {
        Map<String, JsonNode> remainingNodes = new HashMap<>();

        rootNode.fields()
                .forEachRemaining(entry -> {
                    final String key = entry.getKey();

                    if(key.equals("code") || key.equals("description")) {
                        return;
                    }

                    remainingNodes.put(key, entry.getValue());
                });

        return remainingNodes;
    }
}