2015-12-22 133 views
-1

我正在构建一个有点复杂的API的客户端库。然而,有不同的响应对象,这是这个样子之间显著共性:反序列化Java中展开的/展平的Json

{ 
    "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系统的其余部分,而无需编写重要的附加模块。我错过了什么吗?有没有更好的方法来做这种事情?

+0

对代码有几个小小的改正:Response类的'response'字段应该是'String'类型,有两个'BData'类,所以第二个可能是'CData 'CData'中的''和'c'字段应该是'boolean'类型的。 –

回答

0

这不是@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
这里conly需要注意的是,你的客户需要预先知道响应类型的输出

+0

downvote的任何理由? –

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); 
    } 
}