2013-10-03 21 views
20

私はFlickr APIを使用しています。 flickr.test.loginメソッドを呼び出す場合、デフォルトJSONの結果は次のとおりです。ジャンクションのカスタム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 Stringを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); 

しかし、これは最も非エレガントな方法だと思います。注釈やカスタムデシリアライザを使用する簡単な方法はありますか?

これは私にとって非常に明白だろうが、もちろんそれは動作しません:

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内のユーザー名クラスを作成し、Iドンので、それを

+0

......。唯一の方法は? –

+0

JSONオブジェクトとしてユーザー名を表しますので、あなたがそれをマップするとき、それはオブジェクトとしてマップすることです。これは、しかしひどいではありません:-(ます – tom

+0

True、ただし、必要なものは必要ありません。[機能リクエスト](http://jira.codehaus)があります。このメソッドは、FlickrAccountクラスのgetUsernameメソッドを使用してUsername._content値を返します。 .org/browse/JACKSON-781)は、私が望むことをする機能を記述していますが、まだ開いています。 –

6

を_content場を与えなければなりません「トンだけで、ユーザー名をマッピングするためにカスタムクラス(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()); 

にですまだ私が望むようにエレガントではありませんが、少なくとも私はすべての醜いキャスティングを取り除きました。 このソリューションのもう1つの利点は、私のドメインクラス(FlickrAccount)がジャクソンの注釈で汚染されていないことです。

@Michał Ziober's answerに基づいて、私は自分の意見では最も直感的な解決法を使用することに決めました。ただJsonNode上記のように、

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

をしかし、デシリアライザは、任意の内部クラスを使用していません::カスタムデシリアライザで@JsonDeserialize注釈を使用して

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を使用することもできます。 Noooooo

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