Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a json response with multi type values coming for same field?

How to parse answerData key from json response in kotlin as it is changing its type in each block? I tried keeping it Any but was not able to type cast. how to parse answerData?

{
"status": "OK",
"data": [
    {
        "id": 10,
        "answerData": null
    },
    
    {
        "id": 21,
        "answerData": {
            "selectionOptionId": 0,
            "selectionOptionText": null
        }
    },
    
    {
        "id": 45,
        "answerData": {
            "IsAffiliatedWithSeller": false,
            "AffiliationDescription": null
        }
    },
   
    { 
       "id" : 131, 
       "answerData" : [ 
            { "2" : "Chapter 11" }, 
            { "3" : "Chapter 12" }, 
            { "1" : "Chapter 7" } 
        ] 
    }, 
    
    
     { 
       "id" : 140, 
       "answerData" : [ 
        { 
          "liabilityTypeId" : 2, 
          "monthlyPayment" : 200, 
          "remainingMonth" : 2, 
          "liabilityName" : "Separate Maintenance", 
          "name" : "Two" 

        }, 
        { 
          "liabilityTypeId" : 1, 
          "monthlyPayment" : 300, 
          "remainingMonth" : 1, 
          "liabilityName" : "Child Support", 
          "name" : "Three" 

        } 
      ] 
   } 
 ]
}
like image 766
Muhammad Irfan Avatar asked Jan 19 '26 13:01

Muhammad Irfan


1 Answers

As commented and explained in other answers you really should ask changes to the JSON format. However it is not so unusual to have list of elements of which the data included varies. For such case there should at least be some field indication the type of data to be deserialized. (not saying it is not an anti-pattern sometimes it might be).

If you reach that agreement it is possible to use - for example - RuntimeTypeAdapterFactory like explained in linked question (sorry it is Java).

If not you will run into troubles. It is still quite easy to isolate the problem. Not saying it is easy to solve. I present one possible (sorry again, Java but guess it is easily adaptable to Kotlin) solution. I have used lots of inner static classes to make the code more compact. The actual logic has not so many rows most of the code is to map your JSON into java classes.

Make the model abstract in a way that it does not hinder Gson to do its job whatever it heads in that problematic field:

@Getter @Setter
public class Response {
    private String status;
    @Getter @Setter
    public static class DataItem {
        private Long id;
        // below 2 rows explained later, this is what changes
        @JsonAdapter(AnswerDataDeserializer.class)
        private AnswerData answerData;
    }
    private DataItem[] data;
}

As you see there is declared this AnswerData and @JsonAdapter for handling the actual more complex stuff:

public class AnswerDataDeserializer
    implements JsonDeserializer<AnswerDataDeserializer.AnswerData> {

    private final Gson gson = new Gson();

    // The trick that makes the field more abstract. No necessarily 
    // needed answerData might possibly be just Object
    public interface AnswerData {
        // just to have something here not important
        default String getType() {
            return getClass().getName();
        }
    }
    // here I have assumed Map<K,V> because of field name cannot be plain number.
    @SuppressWarnings("serial")
    public static class ChapterDataAnswer extends ArrayList<Map<Long, String>>
            implements AnswerData {
    }

    @SuppressWarnings("serial")
    public static class LiabilityDataAnswer
        extends ArrayList<LiabilityDataAnswer.LiabilityData>
            implements AnswerData {
        @Getter @Setter
        public static class LiabilityData {
            private Long liabilityTypeId;
            private Double monthlyPayment;
            private Integer remainingMonth;
            private String liabilityName;
            private String name;
        }
    }

    @Override
    public AnswerData deserialize(JsonElement json, Type typeOfT, 
            JsonDeserializationContext context)
            throws JsonParseException {
        if(json.isJsonArray()) {
            try {
                return gson.fromJson(json, ChapterDataAnswer.class);                
            } catch (Exception e) {
                return gson.fromJson(json, LiabilityDataAnswer.class);              
            }
        }
        if(json.isJsonObject()) {
            // do something else
        }
        return null;
    }       
}

I have above presented only the two more complex array types. But as you can see you will have to check/peek all the deserialized AnswerData in some way to determine the actual type in method deserialize

Now you need still need to know about different types of AnswerData. Maybe there are such types that collide in a way that you cannot determine the type.

NOTE: you can also always also deserialize whole stuff or any object as a Map or Object (Gson will make it LinkedHashMap if I remember correct)

Whether way you do it you still need to check the instance of the object after deserialization what it is and use cast.

like image 123
pirho Avatar answered Jan 22 '26 10:01

pirho



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!