Darab Javaid
Darab Javaid

Reputation: 145

Error Deserializing JSON Object/Array using newtonsoft

I have an json string which contains the following data

"air:FlightOptionsList": {
    "air:FlightOption": [{
            "LegRef": "hx5kk+3R2BKABGzqAAAAAA==",
            "Destination": "LHE",
            "Origin": "DXB",
            "air:Option": {
                "Key": "hx5kk+3R2BKA/FzqAAAAAA==",
                "TravelTime": "P0DT3H0M0S",
                "air:BookingInfo": {
                    "BookingCode": "I",
                    "BookingCount": "7",
                    "CabinClass": "Economy",
                    "FareInfoRef": "hx5kk+3R2BKAzFzqAAAAAA==",
                    "SegmentRef": "hx5kk+3R2BKAtFzqAAAAAA=="
                }
            }
        }, {
            "LegRef": "hx5kk+3R2BKAFGzqAAAAAA==",
            "Destination": "DXB",
            "Origin": "LHE",
            "air:Option": {
                "Key": "hx5kk+3R2BKACGzqAAAAAA==",
                "TravelTime": "P0DT11H30M0S",
                "air:BookingInfo": [{
                        "BookingCode": "U",
                        "BookingCount": "7",
                        "CabinClass": "Economy",
                        "FareInfoRef": "hx5kk+3R2BKA+FzqAAAAAA==",
                        "SegmentRef": "hx5kk+3R2BKAvFzqAAAAAA=="
                    }, {
                        "BookingCode": "Y",
                        "BookingCount": "9",
                        "CabinClass": "Economy",
                        "FareInfoRef": "hx5kk+3R2BKA+FzqAAAAAA==",
                        "SegmentRef": "hx5kk+3R2BKAxFzqAAAAAA=="
                    }
                ],
                "air:Connection": {
                    "SegmentIndex": "0"
                }
            }
        }
    ]
}

my Class Structure is below:

public class FlightOptionsList
{
    public List<FlightOption> FlightOption { get; set; }
}

public class FlightOption
{
    public string LegRef { get; set; }
    public string Destination { get; set; }
    public string Origin { get; set; }
    public Option Option { get; set; }
}

public class Option
{
    public string Key { get; set; }
    public string TravelTime { get; set; }
    public List<BookingInfo> BookingInfo { get; set; }
    public Connection Connection { get; set; }
}

public class BookingInfo
{
    public string BookingCode { get; set; }
    public string BookingCount { get; set; }
    public string CabinClass { get; set; }
    public string FareInfoRef { get; set; }
    public string SegmentRef { get; set; }
}

I want to deserialize it, but its giving me an error as following:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ParseSoapEnveloperReqRes.BookingInfo]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'FlightOptionsList.FlightOption[0].Option.BookingInfo.BookingCode', line 394, position 59.

this is because, as if you see the json string, FlightOptionsList.FlightOption[0].Option.BookingInfo is an object but in FlightOptionsList.FlightOption[1].Option.BookingInfo is an array as you can see.

how can I set this problem... I am using the following code to deserialize the json string to class object

 var AirTravelResultModel = JsonConvert.DeserializeObject<AirTravelResultModel>(xmlInputData);

Upvotes: 0

Views: 8683

Answers (3)

Patrick Knott
Patrick Knott

Reputation: 1827

I apologize for the redundancy of posting this twice... unfortunately this question is somewhat resolved in its predecessor: Deserializing JSON when sometimes array and sometimes object. I will not use your objects. Ensure your objects are well formed. Sometimes companies may store an object as an array or object depending on whether there is one or many. You may be running into that. Ensure that your converter is returning a list or an array pending your object type. In my instance I needed to return an array.

Expounding upon Martinez and mfanto's answer for Newtonsoft. Their answer does work with Newtonsoft:

Here is an example of doing it with an array instead of a list (and correctly named).

public class SingleValueArrayConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject 
            || reader.TokenType == JsonToken.String
            || reader.TokenType == JsonToken.Integer)
        {
            return new T[] { serializer.Deserialize<T>(reader) };
        }
        return serializer.Deserialize<T[]>(reader);
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

Then over the attribute write this:

[JsonProperty("INSURANCE")]
[JsonConverter(typeof(SingleValueArrayConverter<InsuranceInfo>))]
public InsuranceInfo[] InsuranceInfo { get; set; }

Newtonsoft will do the rest for you.

return JsonConvert.DeserializeObject<T>(json);

Cheers to Martinez and mfanto!

Believe it or not, this will work with sub items. (It may even have to.) So... inside of my InsuranceInfo, if I have another object/array hybrid, use this again on that property like so:

[JsonProperty("CLAIM")]
[JsonConverter(typeof(SingleValueArrayConverter<Claim>))]
public Claim[] ClaimInfo { get; set; }

Due to implementing WriteJson, this will also allow for reserializing the object back to json if necessary, but at that point, it will always be an array (yay!).

Upvotes: 0

CodingYoshi
CodingYoshi

Reputation: 27009

Most of the text in this answer is text from your question with a few tweaks. All you need to do is to fix your JSON structure, and decorate the classes with JsonProperty attribute because your JSON property names have air: in it and : is not allowed in C#. That's all.

Step 1

First thing you need to do fix is your JSON because in one case air:BookingInfo is an object and then in the other case, it is an array. Therefore, its schema is not consistent. The correct JSON would be like this:

{
    "air:FlightOption": [{
            "LegRef": "hx5kk+3R2BKABGzqAAAAAA==",
            "Destination": "LHE",
            "Origin": "DXB",
            "air:Option": {
                "Key": "hx5kk+3R2BKA/FzqAAAAAA==",
                "TravelTime": "P0DT3H0M0S",
                "air:BookingInfo": [{
                    "BookingCode": "I",
                    "BookingCount": "7",
                    "CabinClass": "Economy",
                    "FareInfoRef": "hx5kk+3R2BKAzFzqAAAAAA==",
                    "SegmentRef": "hx5kk+3R2BKAtFzqAAAAAA=="
                }]
            }
        },
        {
            "LegRef": "hx5kk+3R2BKAFGzqAAAAAA==",
            "Destination": "DXB",
            "Origin": "LHE",
            "air:Option": {
                "Key": "hx5kk+3R2BKACGzqAAAAAA==",
                "TravelTime": "P0DT11H30M0S",
                "air:BookingInfo": [{
                        "BookingCode": "U",
                        "BookingCount": "7",
                        "CabinClass": "Economy",
                        "FareInfoRef": "hx5kk+3R2BKA+FzqAAAAAA==",
                        "SegmentRef": "hx5kk+3R2BKAvFzqAAAAAA=="
                    },
                    {
                        "BookingCode": "Y",
                        "BookingCount": "9",
                        "CabinClass": "Economy",
                        "FareInfoRef": "hx5kk+3R2BKA+FzqAAAAAA==",
                        "SegmentRef": "hx5kk+3R2BKAxFzqAAAAAA=="
                    }
                ],
                "air:Connection": {
                    "SegmentIndex": "0"
                }
            }
        }
    ]
}

Step 2

You can create the classes for your JSON easily using Visual Studio as I have shown in this answer.

Step 3

You need to help the serializer a little bit and tell it how to map the JSON properties to your class's properties and for that you can use JsonPropertyAttribute attribute as shown below:

public class FlightOptionsList
{
    [JsonProperty("air:FlightOption")]
    public AirFlightoption[] airFlightOption { get; set; }
}

public class AirFlightoption
{
    public string LegRef { get; set; }
    public string Destination { get; set; }
    public string Origin { get; set; }
    [JsonProperty("air:Option")]
    public AirOption airOption { get; set; }
}

public class AirOption
{
    public string Key { get; set; }
    public string TravelTime { get; set; }
    [JsonProperty("air:BookingInfo")]
    public AirBookingInfo[] airBookingInfo { get; set; }
    [JsonProperty("air:Connection")]
    public AirConnection airConnection { get; set; }
}

public class AirBookingInfo
{
    public string BookingCode { get; set; }
    public string BookingCount { get; set; }
    public string CabinClass { get; set; }
    public string FareInfoRef { get; set; }
    public string SegmentRef { get; set; }
}

public class AirConnection
{
    public string SegmentIndex { get; set; }
}

Step 4

The only code you need is this:

var fol = JsonConvert.DeserializeObject<FlightOptionsList>("YourJSON");

Upvotes: 0

Roger Leblanc
Roger Leblanc

Reputation: 1583

Look at Camilo Martinez answer on this discussion : Deserializing JSON when sometimes array and sometimes object

Basically, you'll need to add the JsonConverter attribute to your BookingInfo property and handle the conversion in a JsonConverter implementation.

public class FlightOptionsList
{
    public List<FlightOption> FlightOption { get; set; }
}

public class FlightOption
{
    public string LegRef { get; set; }
    public string Destination { get; set; }
    public string Origin { get; set; }
    public Option Option { get; set; }
}

public class Option
{
    public string Key { get; set; }
    public string TravelTime { get; set; }
    [JsonConverter(typeof(SingleValueArrayConverter<BookingInfo>))]
    public List<BookingInfo> BookingInfo { get; set; }
    public Connection Connection { get; set; }
}

public class BookingInfo
{
    public string BookingCode { get; set; }
    public string BookingCount { get; set; }
    public string CabinClass { get; set; }
    public string FareInfoRef { get; set; }
    public string SegmentRef { get; set; }
}

public class Connection
{
    public string SegmentIndex { get; set; }
}

And here's the converter :

public class SingleValueArrayConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object retVal = new Object();
        if (reader.TokenType == JsonToken.StartObject)
        {
            T instance = (T)serializer.Deserialize(reader, typeof(T));
            retVal = new List<T>() { instance };
        }
        else if (reader.TokenType == JsonToken.StartArray)
        {
            retVal = serializer.Deserialize(reader, objectType);
        }
        return retVal;
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

Upvotes: 7

Related Questions