0

My problem is that I'm getting this error

"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[api.Controllers.Journey]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly" ... When I try to serialize my API json response.

The API return something like this:

{
"Transport": [
    {
        "$id": "6",
        "SourceID": "1",
        "Context": "1",
        "Id": "AMADEUS#1",
        "Provider": null,
        "Journey": {
            "SourceID": "R1G0S0",
            "Duration": "42000",
            "Id": "5b6db9c6bfac4"
        }

    },
    {
        "$id": "7",
        "SourceID": "1",
        "Context": "1",
        "Id": "AMADEUS#1",
        "Provider": null,
        "Journey": [
            {
                "SourceID": "R1G0S0",
                "Duration": "42000",
                "Id": "5b6db9c6bfac4"
            },
            {
                "SourceID": "R1G0S1",
                "Duration": "42000",
                "Id": "5b6db9c6bsac4"
            }
        ]

    }
]
}

The Journey field is an JObject at first result, but is an JArray on second...

And I'm getting the error when I deserialize:

Transport Transport = JsonConvert.DeserializeObject<Transport>(json_response);

My class properties:

public class Transport{
    public string SourceID { get; set; }
    public string Context { get; set; }
    public string Id { get; set; }
    public ProviderOD Provider { get; set; }
    public Journey[] Journey { get; set; }
    public PriceOD Price { get; set; }
}
public class Journey
{
    public string Id { get; set; }
    public string SourceID { get; set; }
    public string Duration { get; set; }
}

What should I do, to set Journey[ ] or Journey dynamically ??

Thanks in advance ...

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300

3 Answers3

2

Your class declaration is not right, you have to use the following to decode

TransportRootObject Transport = JsonConvert.DeserializeObject<TransportRootObject>(json_response);

and this class(es) declaration

public class TransportRootObject
{
    public List<Transport> Transport { get; set; }
}
public class Transport
{
    [JsonProperty("$id")]
    public string Id { get; set; }
    public string SourceID { get; set; }
    public string Context { get; set; }
    public string Id { get; set; }
    public ProviderOD Provider { get; set; }
    public List<Journey> Journey { get; set; }
}
public class Journey
{
    public string Id { get; set; }
    public string SourceID { get; set; }
    public string Duration { get; set; }
}
cramopy
  • 3,459
  • 6
  • 28
  • 42
  • You're right, thanks, but I'm still getting the error... The real problem is when serialize the first journey is not a List. So thats why is giving me this "Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[api.Controllers.Journey]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly" – Aly Daniel Zuniaga Ramos Aug 10 '18 at 16:47
  • @AlyDanielZuniagaRamos the problem here is, that JSON does not allow a *comma* after the last element. Thus `"Id": "5b6db9c6bfac4", }` has to get rid of the last *comma* before the `}` to become valid. This has to be done at all instances of `, }`. – cramopy Aug 10 '18 at 16:52
  • No man, that was my error typing here... but not solved my problem... Thanks – Aly Daniel Zuniaga Ramos Aug 10 '18 at 16:59
  • @AlyDanielZuniagaRamos Okay, then there is another problem. please see https://stackoverflow.com/questions/44100383/handling-json-single-object-and-array Your JSON is not static within its structure. Please see the provided link and feel free to upvote and mark my answer as correct. – cramopy Aug 10 '18 at 17:04
0

So there's a few issues here. Your top level Transport object in your JSON is actually an array of transport objects. So you need an extra class:

public class DocumentRoot
{
    public List<Transport> Transport { get; set; }
}

Then you want:

var DocumentRoot = JsonConvert.DeserializeObject<DocumentRoot>(json_response);
//DocumentRoot.Transport is now a List<Transport> of your transport elements from the document.

In your JSON one of the member names is "$id" which of course isn't valid in C#, so to make this behave you need to add a [JsonProperty(PropertyName = "$id")] attribute on your Id property to tell the serializer the name.

PhonicUK
  • 13,486
  • 4
  • 43
  • 62
  • You're right, thanks, but I'm still getting the error... The real problem is when serialize the first journey is not a List. So thats why is giving me this "Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[api.Controllers.Journey]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly" – Aly Daniel Zuniaga Ramos Aug 10 '18 at 16:47
0

I think your issue is that you've got poorly formatted JSON being returned from your API. If Journey is an array, it should always return as an array/list...not an object if there is only 1 and then an array if there is more than one.

If this is a third party API that you are unable to fix then your next option is to use a custom converter to handle weirdly formatted json like this.

I don't recall where i found the original source/solution for this but I'll walk you through the steps.

First you'll need to create a new class such as this.

public 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();
    }
}

This is your custom converter to handle a "sometimes array, sometimes object" situation. It pretty much just does a test to determine if its an array or an object and handles accordingly.

Then, within your transport class, you'll add the attribute above the Journey array property like so.

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

This should then handle situations where journey comes through as an array or as an object.