Beena
Beena

Reputation: 2354

Deserialize json with same key but different type in android using Jackson

I am calling web-services which can have 2 types of json object in response. Now sometimes i get key profile with type String and sometimes it may have same key with type 'ProfileSubObject'. So how to manage this case? Below are my two types of object. I am using Jackson library to parse json.

1.)

{
  "data": [
    {
      "profession": "iOS Developer",
      "thanks": {
        "count": 5
      },
      "profile": "test"
    }
  ]
}

2.)

{
  "data": [
    {
      "profession": "iOS Developer",
      "thanks": {
        "count": 5
      },
      "profile": {
        "val1":"test1",
        "val2":"test2"
      }
    }
  ]
}

Key profile have 2 different type of object based on web-service call.

Following is my data class structure.

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DataObject {

 @JsonProperty("profession")
private String profession;

@JsonProperty("profile")
private ProfileObject profile;

@JsonProperty("thanks")
private ThanksObject thanks;

public String getProfession() {
    return profession;
}

public ThanksObject getThanks() {
    return thanks;
}
public ProfileObject getProfile() {
    return profile;
}

}

And Profile class is as per below.

public class ProfileObject {

ProfileObject(){

}

ProfileObject(ProfileSubObject profileSubObject){
    this.profileSubObject= profileSubObject;

}

ProfileObject(String profile){
    this.profile= profile;
}
private  ProfileSubObject profileSubObject;
private String profile;

public ProfileSubObject getProfileSubObject() {
    return profileSubObject;
}
 }

Now when i parse my object, ProfileObject is always null. I want it to get parsed based on proifle key data type.

Anyone could help me with parsing?

Upvotes: 4

Views: 3036

Answers (2)

Sharon Ben Asher
Sharon Ben Asher

Reputation: 14328

In constructing the solution, I faced two problems:

  1. the Json structure does not match a single DataObject
  2. the original problem of deserializing same property into differnt types of Java objects.

The first problem I solved by constructing JavaType objects which tell Jackson the generic type of the collections involved. There are two such collections: a Map, consisting of a single entry with key "data" and value of List of DataObjects

The second problem, I solved with the Jackson feature of @JsonAnySetter which directs Jackson to call a single method for all properties it doesn't recognize. For this purpose, I added @JsonIgnore to the profile variable to make sure that Jackson indeed doesn't recognize it. Now Jackson calls the same method for the two input jsons

This is the new DataObject class:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DataObject
{
    @JsonProperty("profession")
    public String profession;

    @JsonIgnore // forcing jackson to not recognize this property
    public ProfileObject profile;

    @JsonProperty("thanks")
    public ThanksObject thanks;

    public String getProfession() { return profession; }
    public void   setProfession(String p) { profession = p; }

    public ThanksObject getThanks() { return thanks; }
    public void         setThanks(ThanksObject t) { thanks = t; }

    public ProfileObject getProfile() { return profile; }
    public void          setProfile(ProfileObject p) { profile = p; }

    @JsonAnySetter
    public void setProfileFromJson(String name, Object value)
    {
        // if value is single String, call appropriate ctor 
        if (value instanceof String) {
            profile = new ProfileObject((String)value);
        }
        // if value is map, it must contain 'val1',  'val2' entries
        if (value instanceof Map) {
            ProfileSubObject profileSubObject =
                    new ProfileSubObject(((Map<String, String>)value).get("val1"), ((Map<String, String>)value).get("val2"));
            profile = new ProfileObject(profileSubObject);
        }
        // error?
    }
}

Here is my test method, which includes the java type construction I mentioned:

public static void main(String[] args)
{
    try (Reader reader = new FileReader("C://Temp/xx2.json")) {
        ObjectMapper mapper = new ObjectMapper();
        // type of key of map is String  
        JavaType stringType = TypeFactory.defaultInstance().constructType(String.class);
        // type of value of map is list of DataObjects  
        JavaType listOfDataObject = TypeFactory.defaultInstance().constructCollectionType(List.class, DataObject.class);
        // finally, construct map type with key and value types  
        JavaType rootMap = TypeFactory.defaultInstance().constructMapType(HashMap.class, stringType, listOfDataObject);

        Map<String ,List<DataObject>> m = mapper.readValue(reader, rootMap);

        DataObject do1 = m.values()
                // get first (only?) value in map (it is list)
                .stream().findFirst().orElse(Collections.emptyList())
                // get first (only?) item in list - it is the DataObject
                .stream().findFirst().orElse(null);

        System.out.println(do1.profile);
        System.out.println(do1.profile.profile);
        System.out.println(do1.profile.profileSubObject.val1 + " " + do1.profile.profileSubObject.val2);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Upvotes: 4

RTJ
RTJ

Reputation: 1

This may be of help in regards to parsing JSON, use a JsonReader. It does assume you are using RESTful webservice and have already gotten a HttpURLConnection and an InputStream from the connection.

https://developer.android.com/reference/android/util/JsonReader.html

Upvotes: 0

Related Questions