2

I am consuming an api with .NET Core, which returns arrays of objects. But when the array contains only one object, the external API will remove the array, and paste the object directly.

I hope there is an attribute or setting which does not require me to work with JObjects, or override the JsonConverter.

Sample JSON im dealing with:

{
    "a": {
        "b": [{
            "id": 1,
            "name": "first object"
        }, {
            "id": 2,
            "name": "second object"
        }]
    }
}

Json with omitted array

{
    "a": {
        "b": {
            "id": 1,
            "name": "One object only"
        }
    }
}

This is what I am doing (simplified)

public class Response
{
    public A a { get; set; }
}

public class A
{
    public List<B> b { get; set; }
}

public class B
{
    public int id { get; set; }
    public string name { get; set; }
}

var apiResponse = await response.Content.ReadAsAsync<Response>()

I wish it would be possible that when the second JSON example is returned, that ReadAsAsync() would automatically understand that it should be converted to an array with one object of B.

The problem is that this can happen on many places in the API responses.

Joel'-'
  • 652
  • 1
  • 5
  • 17
  • Possible duplicate of [How to handle both a single item and an array for the same property using JSON.net](https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n) – xdtTransform Feb 16 '19 at 10:24

1 Answers1

2

You can create a custom JsonConverter to check whether you are dealing with an array or a single object. The following sample shows a converter that converts to List<T>:

public class ArrayJsonConverter<T> : JsonConverter<List<T>>
{
    public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            var jObj = JObject.Load(reader);
            var obj = jObj.ToObject<T>();
            var lst = new List<T>();
            lst.Add(obj);
            return lst;
        }
        else if (reader.TokenType == JsonToken.StartArray)
        {
            var jArray = JArray.Load(reader);
            return jArray.ToObject<IEnumerable<T>>().ToList();
        }
        throw new InvalidOperationException();
    }

    public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

The converter first checks whether the reader is positioned at an object or an array. Depending on that, it either creates a list with just one object or reads the array.
In order to use it, you can mark the properties with the JsonConverter attribute, e.g.:

public class A
{
    [JsonConverter(typeof(ArrayJsonConverter<B>))]
    public List<B> b { get; set; }
}
Markus
  • 20,838
  • 4
  • 31
  • 55