0

I am consuming an API response and the response contains a list of items. Here is example JSON

{
    "attendeeid": "1",
    "responses": {
    "1": {
        "questionid": "1",
        "fieldname": "1",
        "name": "question?",
        "pageid": "2",
        "page": "Attendee Information",
        "auto_capitalize": "0",
        "choicekey": "",
        "response": [
            " Identify need",
            " Evaluate products and services"
        ]
    },
    "2": {
        "questionid": "2",
        "fieldname": "2",
        "name": "question2",
        "pageid": "2",
        "page": "Attendee Information",
        "auto_capitalize": "0",
        "choicekey": "live",
        "response": "live"
    },
},
}

As you can see, the response field in the response can be an object, or a string.

How can I create a POCO object which can be deserialized into? Currently my class is

    public class RegistrantInfoResponse
    {
        public string questionid { get; set; }
        public string fieldname { get; set; }
        public string name { get; set; }
        public string response { get; set; }
    }

    public class RegistrantInfo
    {
        public string attendeeid { get; set; }
        public List<RegistrantInfoResponse> responses { get; set; }
    }
andrewb
  • 2,995
  • 7
  • 54
  • 95
  • 2
    `response` is not an _object_ or a string, its an _array of strings_ or a single string... To handle that, see [here](https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n) – maccettura Nov 15 '19 at 20:54
  • 1
    You have interesting JSON coming from the API. Typically the questions would be in an array `[{Question:1},{Question:2}]` and using numbers ("1","2") as property key names doesn't make sense. – Pete Nov 15 '19 at 21:00
  • For `responses` use a `Dictionary` or `Dictionary `as shown in [How can I parse a JSON string that would cause illegal C# identifiers?](https://stackoverflow.com/a/24536564/3744182) or [Create a strongly typed c# object from json object with ID as the name](https://stackoverflow.com/a/34213724/3744182). – dbc Nov 15 '19 at 21:26
  • And for `"response"` use `[JsonConverter(typeof(SingleOrArrayConverter))] public List response` from [How to handle both a single item and an array for the same property using JSON.net](https://stackoverflow.com/q/18994685/3744182) as mentioned in comments above. – dbc Nov 15 '19 at 21:31

1 Answers1

0

There are couple of modifications you could in your class definition. The RegistrantInfo.responses should be defined as

public Dictionary<string, RegistrantInfoResponse> responses { get; set; }

This would enable you to use dynamic keys as in the responses (Example, "1","2").

If your observe RegistrantInfoResponse.response, it is either an string or an collection of strings. You could use an Custom Converter as provided in the example here by Brian, which converts a string to a List. This would require to decorate the property with an attribute as the following.

[JsonConverter(typeof(SingleOrArrayConverter<string>))]
public List<string> response { get; set; }

The complete class definition would be as the following.

public class RegistrantInfoResponse
{
    public string questionid { get; set; }
    public string fieldname { get; set; }
    public string name { get; set; }
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string> response { get; set; }
}

public class RegistrantInfo
{
    public string attendeeid { get; set; }
    public Dictionary<string, RegistrantInfoResponse> responses { get; set; }
}

Sharing the CustomConverter defined by Brian

class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Array)
        {
            return token.ToObject<List<T>>();
        }
        return new List<T> { token.ToObject<T>() };
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

You could now deserialize the json as

var result = JsonConvert.DeserializeObject<RegistrantInfo>(json);

Output Sample

enter image description here

Anu Viswan
  • 17,797
  • 2
  • 22
  • 51