Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
198 views
in Technique[技术] by (71.8m points)

java - Deserializing JSON with multiple types in one field

I would like to deserialize JSON (with Jackson 1.9.11 and RestTemplate 1.0.1), in which one field may have more type meanings, for example:

    {"responseId":123,"response":"error"}

or

    {"responseId":123,"response":{"foo":"bar", ... }}

Either one or other case works correctly with one setter of specific type (String od custom Response class), but when I put into my entity bean overriden setter to be able to handle both cases, exception is thrown:

Caused by: org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [xxxx.templates.ExportResponse] and content type [application/json;charset=utf-8]

I was thinking about three solutions, but I did not get any of them working:

  • using only String setter and inside use ObjectMapper to unmarshall that string, if it is not equal to "error", but when that JS Array comes, it's not string so no String setter is used :(.
  • use polymorphic type handling (@JsonTypeInfo annotation) with own JsonDeserializer extension - I'm still trying to understand this and implement.
  • create list of HttpMessageConverter and put inside all message converters, I can use. But I thing this step is unnecessary, because only MappingJacksonHttpMessageConverter is used, am I right?

EDIT: how it works now

Setter in entity bean:

@JsonDeserialize(using = ResponseDeserializer.class)
public void setResponse(Object responseObject) {
    if(responseObject instanceof Response)
        response = (Response) responseObject;
}

Deserialize method in ResponseDeserializer:

public Response deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
    Response response = new Response();

    if(JsonToken.START_OBJECT.equals(parser.getCurrentToken())) {
        ObjectMapper mapper = new ObjectMapper();
        response = mapper.readValue(parser, Response.class);
    } else
        throw new JsonMappingException("Unexpected token received.");

    return response;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The only way to achieve that is to use a custom deserializer.

Here is an example:

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addDeserializer(Response.class, new ResponseJsonDeserializer());
mapper.registerModule(testModule);

And here is how to write (how I would write it at least) the deserializer:

class ResponseJsonDeserializer extends JsonDeserializer<Response>  {
  @Override
  public Responsedeserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    Response response = new Response();
    if(jp.getCurrentToken() == JsonToken.VALUE_STRING) {
        response.setError(jp.getText());
    } else {
       // Deserialize object
    }
    return response;
  }
}

class Response {
   private String error;
   private Object otherObject; // Use the real type of your object

   public boolean isError() {
      return error != null;
   }

   // Getters and setters

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...