26

I have a JSON like this:

[{ 
    "agencyId": "myCity",
    "road": {
    "note": "",
        "lat": "45.321",
        "lon": "12.21",
        "streetCode": "290",
        "street": "street1",
        "fromNumber": "",
        "toNumber": "",
        "fromIntersection": "",
        "toIntersection": ""
    },
    "changeTypes": ["PARKING_BLOCK", "ROAD_BLOCK"],
},]

and a class like this:

public class AlertRoad : BaseAlert
{
    [JsonProperty("agencyId")]
    [JsonConverter(typeof(StringEnumConverter))]
    public AgencyType AgencyId { get; set; }

    [JsonProperty("changeTypes")]
    [JsonConverter(typeof(StringEnumConverter))]
    public ChangeType[] ChangeTypes { get; set; }

    [JsonProperty("road")]
    public Road RoadInfo { get; set; }
}

AgencyType is an enumeration, and deserializiation and serialization for AgencyId works.

ChangeType is another enumeration, but deserializiation and serialization for ChangeTypes doesn't work. I assume the reason is that ChangeTypes is an array of enumeration values.

The question is: how can I deserialize/serialize ChangeTypes field, or in general an array of enumeration values?

I tried by defining my own field converter, called ChangeTypeConverter, and changing StrinEnumConverter to ChangeTypeConverter for ChangeTypes field, but in the ReadJson function the value of reader is null.

public class ChangeTypeConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var val = reader.Value;
        //val is null?!?
        return val;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ChangeType);
    }
}
Brian Rogers
  • 110,187
  • 27
  • 262
  • 261
panizza
  • 295
  • 3
  • 6

2 Answers2

58

The StringEnumConverter expects only a single enumeration value. Because ChangeTypes is an array, you need to annotate the property a little differently to make it work.

Try this instead:

[JsonProperty("changeTypes", ItemConverterType=typeof(StringEnumConverter))]
public ChangeType[] ChangeTypes { get; set; }
Brian Rogers
  • 110,187
  • 27
  • 262
  • 261
  • 5
    Instead of JsonProperty on the property, a cleaner approach is to **use StringEnumConverter attribute over enum itself**. I prefer it since my case I didn't want to have an alias to my property name and if we need to use ItemConverterType, then the only way is to use JsonProperty and unnecessary provide alias name. – Jimit.Gandhi Jun 29 '19 at 06:42
  • What if it's a `DataMember` attribute? – Panzercrisis Sep 28 '20 at 18:58
15

There is no need to write a custom JsonConverter for serializing/deserializing array of Enum. Instead of decorating individual property within parent model, just decorate the Enum with a StringEnumConverter JsonConverter attribute.

For eg:-

Following Environment model has Shelter enum property and array of enum Shelter[]

public class Environment
{

    public string Name { get; set; }
    public Shelter Shelter { get; set; }
    public Shelter[] Shelters { get; set; }
}

[JsonConverter(typeof(StringEnumConverter))]
public enum Shelter
{
    Indoor,
    Outdoor
}

Output json:-

 {
   "name": "",
   "shelter": "Indoor",
    "shelters": [
       "Indoor",
       "Outdoor"
  ]
 }
demonicdaron
  • 1,236
  • 14
  • 27
Jimit.Gandhi
  • 321
  • 2
  • 8
  • 1
    Note it may not always be possible to decorate the enum directly with an attribute, e.g. if it is defined in a third-party library. A good example is [`System.Windows.Media.Colors`](https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.colors?view=netframework-4.8) – Brian Rogers Oct 16 '19 at 16:59