Serialisera Type-Safe Enums i ASP.NET Web API 2

2015-07-02

Hur man anpassar den inbyggda serialiseringen i MVC 5 / Web API 2 till att även omfatta Type-Safe Enums

Den inbyggda serialiseringen av data i Web API har ändrats sedan en tid tillbaka, och det var inte helt lätt att hitta hur man skulle göra om man kör på MVC 5 / Web API 2.

Sedan MVC 5 kan man numera serialisera enums, men för oss, som har blivit stora anhängare av Type Safe Enum-pattern är det inte så enkelt.

Såhär går man tillväga:

Först, vår Type Safe Enum:

using System;
using System.Collections.Generic;

namespace Viola.CallSlipFetching
{
    public sealed class CallSlipDeliveryTypes
    {
        private readonly string _name;
        private static readonly Dictionary<string, CallSlipDeliveryTypes> Instance = new Dictionary<string, CallSlipDeliveryTypes>();

        private CallSlipDeliveryTypes(string name)
        {
            _name = name;
            Instance[name] = this;
        }

        public static explicit operator CallSlipDeliveryTypes(string str)
        {
            CallSlipDeliveryTypes result;
            if (Instance.TryGetValue(str, out result))
                return result;
            else
                throw new InvalidCastException();
        }

        /// <summary>
        /// FJärrut
        /// </summary>
        public static readonly CallSlipDeliveryTypes ILLOUT = new CallSlipDeliveryTypes("Fjärrut fysiskt material");

        /// <summary>
        /// Kopia
        /// </summary>
        public static readonly CallSlipDeliveryTypes COPY = new CallSlipDeliveryTypes("Fjärrut kopia");

        /// <summary>
        /// Magasinsbeställning
        /// </summary>
        public static readonly CallSlipDeliveryTypes STACKCALL = new CallSlipDeliveryTypes("Magasin");

        /// <summary>
        /// Direktleverans
        /// </summary>
        public static readonly CallSlipDeliveryTypes DIRECTDELIVERY = new CallSlipDeliveryTypes("Direktleverans");

        /// <summary>
        /// Sakanade
        /// </summary>
        public static readonly CallSlipDeliveryTypes MISSING = new CallSlipDeliveryTypes("Saknade");

        ///<summary>
        /// Saknade
        /// </summary>
        public static readonly CallSlipDeliveryTypes MISSINGSEARCHEDONCE = new CallSlipDeliveryTypes("MissingSearchedOnce");

        public override String ToString()
        {
            return _name;
        }
    }
}
Gist

När ASP.Net försöker serialisera denna blir det tvärstopp. Dessa tvärstopp kan man hantera på olika sätt. En approach är att dekorera klassen eller anpassa den på ett sätt som fungerar för default-deserialiseringen genom att lägga till en publik kontruktor mm. Vi ville behålla klassen så långt det var möjligt och istället justera serialiseringen.

Eftersom det numera är JSON.Net som gäller för serialiseringar i Web Api numera är det första steget att implementera en JsonConverter:
using System;
using Newtonsoft.Json;

namespace Viola.CallSlipFetching
{
    public class CallSlipDeliveryTypeConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var deliveryType = value as CallSlipDeliveryTypes;
            writer.WriteValue(deliveryType != null ? deliveryType.ToString() : string.Empty);
           
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (typeof (CallSlipDeliveryTypes).IsAssignableFrom(objectType))
                return (CallSlipDeliveryTypes) (reader.Value as string);
            else return null;
        }

        public override bool CanConvert(Type objectType)
        {
            return typeof(CallSlipDeliveryTypes).IsAssignableFrom(objectType); 
        }
    }
}
Gist

Men hur får man Web API att den ska använda vår Json Converter? Det gör man i WebApiConfig.cs med följande rader:

using System.Web.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Viola.CallSlipFetching;
using ViolaGui.Filters;

namespace ViolaGui
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            /* Other stuff goes here*/
            
            
            var formatters = GlobalConfiguration.Configuration.Formatters;
            var jsonFormatter = formatters.JsonFormatter;
            var settings = jsonFormatter.SerializerSettings;
            settings.Converters.Add(new CallSlipDeliveryTypeConverter());
            settings.Formatting = Formatting.Indented;
            settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        
        /* Other stuff goes also here*/

        }
    }
}

Gist

Enkelt när man kan det och hittar rätt.





 

/Bibliotekarien - The Librarian