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;
}
}
@JsonDeserialize(using = ResponseDeserializer.class)
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;
}
}
For example,
Response.of(ResponseInfo.of(0, "OK"), User.of("Oleg", 23))
will be serialized into:
{
"age": 23,
"code": 0,
"description": "OK",
"name": "Oleg"
}
public class ResponseDeserializerTest {
@Test
public void deserialization() throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
final String json = "{\"code\":0,\"description\":\"OK\",\"payload\":\"Hello World\"}";
final TypeReference<Response<String>> typeReference = new TypeReference<Response<String>>() {
};
Response<String> response = objectMapper.readValue(json, typeReference);
assertThat(response)
.isEqualTo(
Response.of(
ResponseInfo.of(0, "OK"),
"Hello World"
)
);
}
}
public class ResponseDeserializer
extends JsonDeserializer<Response<?>>
implements ContextualDeserializer {
private JsonDeserializer<?> payloadDeserializer;
@SuppressWarnings("unused")
public ResponseDeserializer() {
}
private ResponseDeserializer(JsonDeserializer<?> payloadDeserializer) {
this.payloadDeserializer = payloadDeserializer;
}
@Override
public JsonDeserializer<?> createContextual(
DeserializationContext context,
BeanProperty beanProperty
) throws JsonMappingException {
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);
final JsonDeserializer<Object> payloadDeserializer
= context.findRootValueDeserializer(payloadType);
return new ResponseDeserializer(payloadDeserializer);
}
@Override
public Response<?> deserialize(
JsonParser jsonParser,
DeserializationContext context
) throws IOException, JsonProcessingException {
final ObjectCodec codec = jsonParser.getCodec();
JsonNode root = codec.readTree(jsonParser);
final ResponseInfo responseInfo = ResponseInfo.of(
root.get("code").asInt(),
root.get("description").asText()
);
final JsonNode payloadNode = root.get("payload");
final JsonParser payloadParser = payloadNode.traverse();
final Object payload = payloadDeserializer.deserialize(
payloadParser,
context
);
return Response.of(responseInfo, payload);
}
}
java.lang.NullPointerExceptionat com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:57)at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:10)at org.overmind.br.response.ResponseDeserializer.deserialize(ResponseDeserializer.java:75)at org.overmind.br.response.ResponseDeserializer.deserialize(ResponseDeserializer.java:17)...