C# Newtonsoft serialize property that is an array and a string

IdentityServer4 has a strange way of returning roles. If there is only one role, the property is a string:

{
    "sub": "55568182-9273-4c6f-8d61-6555f7f02551",
    "role": "role001"
}

But if there is more than one role, the property is an array of strings:

{
    "sub": "55568182-9273-4c6f-8d61-6555f7f02551",
    "role": [
        "role001",
        "role002"
    ]
}

If the property is a string, and you try to serialize this into an IEnumerable<string>, you get the following error:

Could not cast or convert from System.String to System.Collections.Generic.IEnumerable`1[System.String].

JsonSerializationException: Error converting value “xxx” to type ‘System.Collections.Generic.IEnumerable`1[System.String]

Or

Could not cast or convert from System.String to System.String[].

JsonSerializationException: Error converting value “xxx” to type ‘System.String[]’

To serialize this into a model class, you will need to implement a JsonConverter, which is a custom converter that, in this case, checks if the property is one object or several before serializing.

STEP 1: IMPLEMENT THE CUSTOM CONVERTER

This JsonConverter checks the property before serializing to see if the property is an array or not. If not, it will output the single item as an array:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace MyCode
{
  internal class CustomArrayConverter<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.ToObjec<T>() };
    }

    public override bool CanWrite
    {
      get
      {
        return false;
      }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
      throw new NotImplementedException();
    }
  }
}

STEP 2: USE THE CUSTOM CONVERTER IN THE MODEL CLASS:

The custom converter is used as a property on the attribute:

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace MyCode
{
  [Serializable]
  public class UserInfoModel
  {
    [JsonProperty("sub")]
    public string ID { get; set; }

    [JsonProperty("role")]
    [JsonConverter(typeof(CustomArrayConverter<string>))]
    public IEnumerable<string> Roles { get; set; }
  }
}

The CustomArrayConverter will work on any type.

MORE TO READ: 

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in .net, .NET Core, c#, General .NET and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.