2013-10-03 38 views
20

我使用的是Flickr API。當調用flickr.test.login方法,默認JSON的結果是:Jackson的自定義JSON反序列化操作

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

我想解析這個響應轉換成Java對象:

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

的JSON屬性應該被映射這樣的:

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

不幸的是,我無法找到一個不錯的,優雅的方式來做到這一點使用註釋。到目前爲止,我的方法是將JSON字符串讀入Map<String, Object>並從那裏獲取值。

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

但我認爲,這是最不優雅的方式,永遠。有沒有簡單的方法,使用註釋或自定義的Deserializer?

這將是對我來說非常明顯,當然這是行不通的:

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

回答

30

您可以爲此類編寫定製的解串器。它可能看起來像這樣:

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

之後,您必須爲您的類定義序列化程序。你可以這樣做:

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

謝謝。我在這裏失蹤的部分是註釋。儘管對象是在模塊中註冊的類型的子類,但我必須提供@JsonDeserialize註釋。 – th3morg

0

你必須做用戶名在類中FlickrAccount,並給它一個_content場

+0

Noooooo ...... :-(這是唯一的方法是什麼? –

+0

的JSON表示用戶名作爲對象,所以當你映射,它映射爲對象,這並不可怕,雖然,你可以在FlickrAccount類中放置一個getUsername方法,該方法返回Username._content值,因此在使用時它將是透明的。 – tom

+0

正確,但不是我想要的或需要的。有[功能請求](http://jira.codehaus .org/browse/JACKSON-781)描述了一個功能,它可以做我想做的,但它仍然是開放的。 –

6

因爲我不「噸要實現自定義類(Username)只是映射的用戶名,我用更優雅的一點點去了,但仍然很醜陋的做法:

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

這是仍然不如我所希望的那麼優雅,但至少我擺脫了所有醜陋的鑄造。 此解決方案的另一個優點是,我的域名類(FlickrAccount)沒有被任何Jackson註釋污染。

根據@Michał Ziober's answer,我決定使用 - 在我看來 - 最直接的解決方案。使用@JsonDeserialize批註與自定義解串器:

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

但解串器不使用任何內部類,只是如上面JsonNode

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

你也可以使用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);