Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing both Array and single object

API retuned json object like below 2 forms.

Form 1

{
"Pricing": [
    {
        "total": 27,
        "currency": "USD",
        "charges": [ //Chargers Array
            {
                "code": "C1",
                "currency": "USD",
                "rate": 15
            },
            {
                "code": "C45",
                "currency": "USD",
                "rate": 12
            }
        ]
    }
  ]
}

Form 2

{
"Pricing": [
    {
        "total": 12,
        "currency": "USD",
        "charges": { //Chargers single object
            "code": "C1",
            "currency": "USD",
            "rate": 12
        }
    }
  ]
}

As you can see sometime chargers object return with array and some times not. My question is how to parse this to C# class object? If I added the C# class like below it cannot be parse properly for Form 2. (Form 1 parsing properly)

public class Charge
{
    public string code { get; set; }
    public string currency { get; set; }
    public decimal rate { get; set; }
}

public class Pricing
{
    public decimal total { get; set; }
    public string currency { get; set; }
    public List<Charge> charges { get; set; } //In Form 2, this should be single object
}

public class MainObj
{
    public List<Pricing> Pricing { get; set; }
}

Error occurred when parse with Newtonsoft deserialization.

MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);

Error

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Charge]' 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 'Pricing[0].charges.code', line 1, position 69.

Any common method for parsing, when receiving different type of object types with C#?

(I look into this as well but it's for java. And most of this kind of question raised for java but not C#.)

like image 835
weeraa Avatar asked Mar 20 '26 01:03

weeraa


1 Answers

Yet another way of dealing with this problem is to define a custom JsonConverter which can handle both cases.

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

    public override bool CanConvert(Type objectType)
        => objectType == typeof(List<T>);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        =>throw new NotImplementedException(); 
}
  • Inside the ReadJson first we get a JToken to be able to determine the read value's Type (kind)
    • Based on that the we can either call ToObject<List<T>> or ToObject<T>
  • Inside the CanConvert we examine that the to be populated property's type is a List<T>
    • Even though there is a generic JsonConverter<T> where you don't have to define the CanConvert, its ReadJson can be implemented in a bit more complicated way
  • Since the question is all about deserialization I've not implemented the WriteJson method
    • You might also consider to override the CanWrite property of the base class to always return false

With this class in our hand you can decorate your properties with a JsonConverterAttribute to tell to the Json.NET how to deal with those properties

public class Pricing
{
    public decimal total { get; set; }
    public string currency { get; set; }

    [JsonConverter(typeof(ArrayOrObjectConverter<Charge>))]
    public List<Charge> charges { get; set; } 
    
    ...
}
like image 163
Peter Csala Avatar answered Mar 22 '26 14:03

Peter Csala



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!