2

I have a class:

 public class MyClass
 {
    public MyEnum Foo{ get; set; }
 }

During serialization i'd like to change the output from

{
   "Foo": 1
}

to

{
   "Foo": "EnumName"
}

I've tried creating an IValueProvider but hit dead ends every way I go. (My scenario is a bit more complicated than stated; I need to find a way to do this entirely within the IContractResolver.)

dbc
  • 104,963
  • 20
  • 228
  • 340
Stringer Bell
  • 265
  • 1
  • 4
  • 13
  • 1
    Why aren't you just using [`StringEnumConverter`](https://www.newtonsoft.com/json/help/html/t_newtonsoft_json_converters_stringenumconverter.htm)? See e.g. [Serialize enum as a string in JSON.NET using attributes](https://stackoverflow.com/q/10387243/3744182) or [How to tell Json.Net globally to apply the StringEnumConverter to all enums](https://stackoverflow.com/q/7427909/3744182). – dbc Sep 15 '17 at 05:03
  • 1
    My scenario is a bit more complicated than stated so I'm open to using that converter if it's possible to do it within the IContractResolver. Would it be possible to add the converter somehow within CreateProperty when the type is a match? – Stringer Bell Sep 15 '17 at 09:59
  • Yes that could be done. Better to add it to the contract for the enum itself in case it's added to an array or dictionary. – dbc Sep 15 '17 at 10:08

1 Answers1

2

You could create a custom ContractResolver inheriting from DefaultContractResolver that automatically applies StringEnumConverter to every contract for an enum or nullable enum:

public class StringEnumContractResolver : DefaultContractResolver
{
    readonly StringEnumConverter converter;

    public StringEnumContractResolver() : this(true, false) { }

    public StringEnumContractResolver(bool allowIntegerValue, bool camelCaseText)
    {
        this.converter = new StringEnumConverter { AllowIntegerValues = allowIntegerValue, CamelCaseText = camelCaseText };
    }

    protected override JsonPrimitiveContract CreatePrimitiveContract(Type objectType)
    {
        var contract = base.CreatePrimitiveContract(objectType);
        var type = Nullable.GetUnderlyingType(contract.UnderlyingType) ?? contract.UnderlyingType;
        if (type.IsEnum && contract.Converter == null)
            contract.Converter = converter;
        return contract;
    }
}

Notes:

  • If the enum type already has a JsonConverter applied, that is kept in preference to the default StringEnumConverter.

  • Adding the converter to the JsonPrimitiveContract for the enum itself, rather than to every JsonProperty for members that return the enum, ensures that the converter is applied to enums in collections and dictionaries.

  • The IValueProvider merely provides methods to get and set values and thus is less convenient to this purpose than the converter. You would need to perform a nested serialization and deserialization of the enum value as a JSON string inside it, but it isn't designed for this and so doesn't have access to the JSON reader, writer, or serializer. In addition, there is no value provider for dictionary values or collection items that are enums.

  • You may want to cache the contract resolver for best performance as explained here.

Sample .Net fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340