Search code examples
c#asp.net-web-apijson.netdotnetnuke

DotnetNuke Web Api - JsonConverter does not get used


I'm trying to enforce a use of an IsoDateTimeConverter on a specific property.

I register the routes and the Content Negotiator:

public class RouteMapper : IServiceRouteMapper
    {

        public void RegisterRoutes(IMapRoute mapRouteManager)
        {
            mapRouteManager.MapHttpRoute("SbApi", "default", "{controller}/{action}", new[] { "SbApi.Controllers" });

            GlobalConfiguration.Configuration.Services.Replace(typeof(System.Net.Http.Formatting.IContentNegotiator), new JsonContentNegotiator());
        }
    }


public class JsonContentNegotiator : IContentNegotiator
    {
        public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        {
            var formatter = new JsonMediaTypeFormatter();
            formatter.UseDataContractJsonSerializer = false;
            var isoDateConverter = formatter.SerializerSettings.Converters
                .OfType<IsoDateTimeConverter>()
                .FirstOrDefault();

            if(isoDateConverter!=null){
                formatter.SerializerSettings.Converters.Remove(isoDateConverter);
            }

            formatter.SerializerSettings.Converters.Add(new IsoDateTimeConverter() { DateTimeFormat = "dd-MM-yyyy" }); // custom date format, to see if the converter is used

            var result = new ContentNegotiationResult(formatter, new MediaTypeHeaderValue("application/json"));
            return result;
        }
    }

Attribute used on the property:

[JsonConverter(typeof(IsoDateTimeConverter))]
        public System.DateTime EndDate { get; set; }

The output of the serializer:

"EndDate":"2016-01-01T00:00:00"

How do I make it use the IsoDateTimeConverter?

Edit:

I used a custom IContentNetogiator to enforce use of JSON over XML.

I used the custom datetime format just to see if the converted gets actually used.

What I really need is an ISO format (with the full timezone) but I'm not able to enforce it for some reason. I tried setting it up in the CustomDate, but to no avail.

public CustomDatetimeConverter()
        {
            base.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; // none of these work..
            base.DateTimeFormat = "o";

            base.Culture = System.Globalization.CultureInfo.InvariantCulture;
            base.DateTimeStyles = System.Globalization.DateTimeStyles.AssumeLocal;
        }

Edit 2: Anyway, I got it working, sort of. Since nothing was working and I don't need the millisecond part of the time, I ended using the following format: "yyyy'-'MM'-'dd'T'HH':'mm':'ss.000000zzz" . The "zzz" is used in place of "K" which for some reason doesn't work.


Solution

  • There are a couple of ways to solve this. Neither involves replacing the IContentNegotiator. The first (and easiest) way is to create a subclass of the IsoDateTimeConverter having your custom date format:

    class CustomDateTimeConverter : IsoDateTimeConverter
    {
        public CustomDateTimeConverter()
        {
            base.DateTimeFormat = "dd-MM-yyyy";
        }
    }
    

    Then change the [JsonConverter] attribute on your EndDate property:

    [JsonConverter(typeof(CustomDateTimeConverter))]
    public System.DateTime EndDate { get; set; }
    

    Another way to do the same thing is to create a custom IContractResolver that will apply the IsoDateTimeConverter to your target property programmatically. Below is the code you would need for the resolver. Replace Your_Class with the name of the class containing the EndDate property.

    class CustomResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type,
                           Newtonsoft.Json.MemberSerialization memberSerialization)
        {
            IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
            if (type == typeof(Your_Class))
            {
                JsonProperty prop = 
                    props.Where(p => p.PropertyName == "EndDate")
                         .FirstOrDefault();
    
                if (prop != null)
                {
                    prop.Converter = 
                         new IsoDateTimeConverter { DateTimeFormat = "dd-MM-yyyy" };
                }
            }
            return props;
        }
    }
    

    To install this resolver in your project, add the following line to the Register method of your WebApiConfig class:

    config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = 
                                                                    new CustomResolver();