lunes, 28 de octubre de 2019

Newtonsoft Json.NET + JsonApiSerializer: Path 'data'...type requires a JSON array

Sample code

    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static void Main(string[] args)
        {
            var cats = ProcessCats().Result;

            foreach (var cat in cats)
            {
                Console.WriteLine(cat);
                Console.WriteLine();
            }
        }

        private static async Task<Cat[]> ProcessCats()
        {
            client.DefaultRequestHeaders.Accept.Clear();
//...

            var stream = await client.GetStreamAsync("https://myapi/cats");

            var serializerSettings = new JsonApiSerializerSettings();
            var serializer = JsonSerializer.CreateDefault(serializerSettings);

            using (var textReader = new StreamReader(stream))
            using (var jsonReader = new JsonTextReader(textReader))
                return serializer.Deserialize<Cat[]>(jsonReader);

        }
    }

   public class Cat
    {
        public string Name { get; set; }

        public override string ToString()
        {
            var sb = new StringBuilder();
            sb.Append("class Cat {\n");
            sb.Append("  Name: ").Append(Name).Append("\n");
            sb.Append("}\n");
            return sb.ToString();
        }
    }


Error

Unhandled Exception: System.AggregateException: One or more errors occurred. (Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Cat[]' 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 'data', line 1, position 8.) ---> Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Cat[]' 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 'data', line 1, position 8.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
   at WebAPIClient.Program.ProcessCats() in /Users/esteban-work/tmp/delete/samples/csharp/getting-started/console-webapiclient/Program.cs:line 42
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at WebAPIClient.Program.Main(String[] args) in /Users/esteban-work/tmp/delete/samples/csharp/getting-started/console-webapiclient/Program.cs:line 17

Fix

Remember to check if the root element has an Id property.

   public class Cat
    {
        public string Id { get; set; }
        public string Name { get; set; }

        public override string ToString()
        {
            var sb = new StringBuilder();
            sb.Append("class ContactForClient {\n");
            sb.Append("  Id: ").Append(Id).Append("\n");
            sb.Append("  Name: ").Append(Name).Append("\n");
            sb.Append("}\n");
            return sb.ToString();
        }
    }


References