2013-10-03 2 views
20

나는 Flickr API을 사용하고 있습니다. flickr.test.login 메소드를 호출 할 때, 기본 JSON 결과는 다음과 같습니다잭슨 사용자 지정 JSON 비 직렬화

{ 
    "user": { 
     "id": "[email protected]", 
     "username": { 
      "_content": "jamalfanaian" 
     } 
    }, 
    "stat": "ok" 
} 
내가 자바 객체로이 응답 구문 분석하고 싶습니다

:

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

그런 다음 클래스에 대한 serializer를 정의해야합니다. 이렇게하면 다음과 같이 할 수 있습니다.

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

감사합니다. 내가 빠뜨린 부분은 주석이었습니다. 객체가 모듈에 등록 된 유형의 서브 클래스 임에도 불구하고 @JsonDeserialize 주석을 제공해야했습니다. – th3morg

0

을 당신은 FlickrAccount 내 이름을 클래스를 만들고 그것을 _content 필드

+0

....... 유일한 방법은? –

+0

json으로 개체로 이름을 나타내는, 그래서 당신이 그것을지도 할 때, 그것은 개체로 매핑인가요 그것은,하지만 끔찍한 아니다 :-(당신 Use Username._content 값을 반환하는 FlickrAccount 클래스에 getUsername 메소드를 넣을 수 있습니다. – tom

+0

실제로 필요하지만 원하는 것은 아닙니다. [기능 요구] (http : //jira.codehaus)가 있습니다. .org/browse/JACKSON-781)은 내가 원하는 것을 할 것이지만 여전히 열려있다. –

6

I 돈 이후를 제공해야

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

그것은이다 '는 t 내가 좀 더 우아한 갔다, 단지 이름을 매핑 할 사용자 정의 클래스 (Username)를 구현하고자하지만, 여전히 매우 추한 방법 아직도 내가 바라는만큼 우아하지는 않지만, 적어도 나는 못생긴 주물을 제거했다. 이 솔루션의 또 다른 장점은 내 도메인 클래스 (FlickrAccount)가 Jackson 주석으로 오염되지 않는다는 것입니다.

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