2013-10-03 16 views
20

Tôi đang sử dụng Flickr API. Khi gọi phương thức flickr.test.login, kết quả JSON mặc định là:Tùy chỉnh JSON Deserialization với Jackson

{ 
    "user": { 
     "id": "[email protected]", 
     "username": { 
      "_content": "jamalfanaian" 
     } 
    }, 
    "stat": "ok" 
} 

Tôi muốn phân tích phản ứng này vào một đối tượng Java:

public class FlickrAccount { 
    private String id; 
    private String username; 
    // ... getter & setter ... 
} 

Các thuộc tính JSON nên được ánh xạ như thế này:

"user" -> "id" ==> FlickrAccount.id 
"user" -> "username" -> "_content" ==> FlickrAccount.username 

Thật không may, tôi không thể tìm thấy cách tốt đẹp, thanh lịch để thực hiện việc này bằng Chú thích. Cách tiếp cận của tôi cho đến nay là đọc chuỗi JSON vào một số Map<String, Object> và nhận các giá trị từ đó.

Map<String, Object> value = new ObjectMapper().readValue(response.getStream(), 
     new TypeReference<HashMap<String, Object>>() { 
     }); 
@SuppressWarnings("unchecked") 
Map<String, Object> user = (Map<String, Object>) value.get("user"); 
String id = (String) user.get("id"); 
@SuppressWarnings("unchecked") 
String username = (String) ((Map<String, Object>) user.get("username")).get("_content"); 
FlickrAccount account = new FlickrAccount(); 
account.setId(id); 
account.setUsername(username); 

Nhưng tôi nghĩ, đây là cách không thanh lịch nhất từ ​​trước tới nay. Có cách nào đơn giản, hoặc bằng cách sử dụng chú thích hoặc Deserializer tùy chỉnh?

Đây sẽ là rất rõ ràng đối với tôi, nhưng tất nhiên nó không hoạt động:

public class FlickrAccount { 
    @JsonProperty("user.id") private String id; 
    @JsonProperty("user.username._content") private String username; 
    // ... getter and setter ... 
} 

Trả lời

30

Bạn có thể viết deserializer tùy chỉnh cho lớp này. Nó có thể trông giống như sau:

class FlickrAccountJsonDeserializer extends JsonDeserializer<FlickrAccount> { 

    @Override 
    public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
     Root root = jp.readValueAs(Root.class); 

     FlickrAccount account = new FlickrAccount(); 
     if (root != null && root.user != null) { 
      account.setId(root.user.id); 
      if (root.user.username != null) { 
       account.setUsername(root.user.username.content); 
      } 
     } 

     return account; 
    } 

    private static class Root { 

     public User user; 
     public String stat; 
    } 

    private static class User { 

     public String id; 
     public UserName username; 
    } 

    private static class UserName { 

     @JsonProperty("_content") 
     public String content; 
    } 
} 

Sau đó, bạn phải xác định bộ nối tiếp cho lớp của mình. Bạn có thể thực hiện việc này theo cách này:

@JsonDeserialize(using = FlickrAccountJsonDeserializer.class) 
class FlickrAccount { 
    ... 
} 
+1

Cảm ơn bạn. Phần mà tôi đã bỏ lỡ ở đây là chú thích. Tôi đã cung cấp chú thích @JsonDeserialize mặc dù đối tượng là một lớp con của một loại được đăng ký trong một mô-đun. – th3morg

0

Bạn phải chắc Tên đăng nhập một lớp trong vòng FlickrAccount và cung cấp cho nó một lĩnh vực _content

+0

Noooooo ...... :-(Có phải đó là cách duy nhất? –

+0

Các JSON đại diện cho tên người dùng như một đối tượng, vì vậy khi bạn ánh xạ nó, nó ánh xạ như một đối tượng. Đó không phải là khủng khiếp, mặc dù bạn có thể đặt một phương thức getUsername trong lớp FlickrAccount trả về giá trị Username._content để sử dụng nó sẽ trong suốt – tom

+0

Đúng, nhưng không phải là thứ tôi muốn hoặc cần. .org/browse/JACKSON-781) mô tả một tính năng, có thể làm những gì tôi muốn, nhưng nó vẫn mở. –

6

Vì tôi don 't muốn thực hiện một lớp tùy chỉnh (Username) chỉ để ánh xạ tên người dùng, tôi đi cùng tao nhã hơn một chút, nhưng cách tiếp cận vẫn còn khá xấu xí:

ObjectMapper mapper = new ObjectMapper(); 
JsonNode node = mapper.readTree(in); 
JsonNode user = node.get("user"); 
FlickrAccount account = new FlickrAccount(); 
account.setId(user.get("id").asText()); 
account.setUsername(user.get("username").get("_content").asText()); 

Đó là vẫn không thanh lịch như tôi mong đợi, nhưng ít nhất tôi đã loại bỏ tất cả những thứ xấu xí. Một ưu điểm khác của giải pháp này là lớp miền của tôi (FlickrAccount) không bị ô nhiễm với bất kỳ chú thích nào của Jackson.

Dựa trên @Michał Ziober's answer, tôi quyết định sử dụng - theo ý kiến ​​của tôi - giải pháp thẳng tiến nhất. Sử dụng một chú thích @JsonDeserialize với một deserializer tùy chỉnh:

@JsonDeserialize(using = FlickrAccountDeserializer.class) 
public class FlickrAccount { 
    ... 
} 

Nhưng deserializer không sử dụng bất kỳ lớp học nội bộ, chỉ là JsonNode như trên:

class FlickrAccountDeserializer extends JsonDeserializer<FlickrAccount> { 
    @Override 
    public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws 
      IOException, JsonProcessingException { 
     FlickrAccount account = new FlickrAccount(); 
     JsonNode node = jp.readValueAsTree(); 
     JsonNode user = node.get("user"); 
     account.setId(user.get("id").asText()); 
     account.setUsername(user.get("username").get("_content").asText()); 
     return account; 
    } 
} 
1

Bạn cũng có thể sử dụng SimpleModule.

SimpleModule module = new SimpleModule(); 
    module.setDeserializerModifier(new BeanDeserializerModifier() { 
    @Override public JsonDeserializer<?> modifyDeserializer(
     DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 
     if (beanDesc.getBeanClass() == YourClass.class) { 
      return new YourClassDeserializer(deserializer); 
     } 

     return deserializer; 
    }}); 

    ObjectMapper objectMapper = new ObjectMapper(); 
    objectMapper.registerModule(module); 
    objectMapper.readValue(json, classType); 
Các vấn đề liên quan