2013-10-03 9 views
20

के साथ कस्टम JSON Deserialization मैं Flickr API का उपयोग कर रहा हूं। जब flickr.test.login विधि बुला, डिफ़ॉल्ट 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 स्ट्रिंग को 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

आप इस वर्ग के लिए कस्टम deserializer लिख सकते हैं। यह इस तरह दिख सकता है:

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

...... :-(कि एक ही रास्ता? –

+0

JSON एक वस्तु के रूप उपयोगकर्ता नाम का प्रतिनिधित्व करता है, इसलिए जब आप यह नक्शा, यह एक वस्तु के रूप में नक्शे है। यह, हालांकि भयानक नहीं है आप FlickrAccount क्लास में getUsername विधि डाल सकता है जो Username._content मान को उपयोग में लाता है, इसलिए यह पारदर्शी होगा। – tom

+0

सच है, लेकिन जो मैं चाहता हूं या नहीं चाहता हूं। एक [सुविधा अनुरोध] है (http: //jira.codehaus .org/ब्राउज़/जैक्सन -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) किसी भी जैक्सन एनोटेशन के साथ प्रदूषित नहीं है।

@Michał Ziober's answer के आधार पर, मैंने अपनी राय में - सबसे सीधे आगे समाधान का उपयोग करने का निर्णय लिया। एक कस्टम deserializer के साथ एक @JsonDeserialize एनोटेशन का उपयोग करना:

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

लेकिन deserializer किसी भी आंतरिक वर्गों का उपयोग नहीं करता, बस 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); 
Noooooo