locked
UTC Date is being deserialized to server local time when PUT and PATCH. RRS feed

  • Question

  • User1649945628 posted

    When I sending data in a HTTP Request with POST method, any date string formatted like "2016-02-05T15:00:00.000Z" is deserialized  correctly as UTC date time at the server side. But with PUT or PATCH method, it will deserialize it as local date time. It extremely annoying when I using ODATA v4 $Patch method sending POST/PUT/PATCH in a same batch and need to twist the date time in order to workaround the bug.

    I noticed that someone had complain about the GET method long ago.

    Can someone comment on the issue?

    Thanks in advance!

    Richard.

    Friday, February 5, 2016 8:10 PM

Answers

  • User36583972 posted

    Hi rtang2011,

    From your description, I suggest you can customize CustomJsonMediaTypeFormatter class. Through this you can correctly handle type, to ensure data consistency before and after.

    The following code for your reference.

    CustomJsonMediaTypeFormatter:

    public class CustomJsonMediaTypeFormatter : MediaTypeFormatter
        {
            public JsonSerializerSettings _jsonSerializerSettings;
            public CustomJsonMediaTypeFormatter()
            {
                _jsonSerializerSettings = CustomJsonSettings.Instance;
                // Fill out the mediatype and encoding we support
                SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
                SupportedEncodings.Add(new UTF8Encoding(false, true));
            }
            public override bool CanReadType(Type type)
            {
                return true;
            }
            public override bool CanWriteType(Type type)
            {
                return true;
            }
            public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
            {
                // Create a serializer
                Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(_jsonSerializerSettings);
                // Create task reading the content
                return Task.Factory.StartNew(() =>
                {
                    using (StreamReader streamReader = new StreamReader(stream, SupportedEncodings.First()))
                    {
                        using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
                        {
                            return serializer.Deserialize(jsonTextReader, type);
                        }
                    }
                });
            }
            public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
            {
                // Create a serializer
                Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(_jsonSerializerSettings);
                // Create task writing the serialized content
                return Task.Factory.StartNew(() =>
                {
                    using (StreamWriter streamWriter = new StreamWriter(stream, SupportedEncodings.First()))
                    {
                        using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter))
                        {
                            serializer.Serialize(jsonTextWriter, value);
                        }
                    }
                });
    
            }
    
        }
    
    
        public static class CustomJsonSettings
        {
            private static JsonSerializerSettings _settings;
            public static JsonSerializerSettings Instance
            {
                get
                {
                    if (_settings == null)
                    {
                        var settings = new JsonSerializerSettings();
                        // Must convert times coming from the client (always in UTC) to local - need both these parts:
                        JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
                        settings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal }); // Critical part 1
                        settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; // Critical part 2
                                                                                    // Skip circular references
                        settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                        // Handle special cases in json (self-referencing loops, etc)
                        //settings.ContractResolver = new CustomJsonResolver();
                        _settings = settings;
                    }
                    return _settings;
                }
            }
        }
    

    WebApiConfig:

    public static void Register(HttpConfiguration config)
            {
                GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
                GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
                GlobalConfiguration.Configuration.Formatters.Add(new CustomJsonMediaTypeFormatter());
    
                ODataModelBuilder builder = new ODataConventionModelBuilder();
                builder.EntitySet<Category>("Category");
                config.MapODataServiceRoute(
                    routeName: "ODataRoute",
                    routePrefix: "odata",
                    model: builder.GetEdmModel());
            }
    

    The tutorial about Using JSON.NET with ASP.NET Web API in the below:

    https://code.msdn.microsoft.com/Using-JSONNET-with-ASPNET-b2423706

    Best Regards,

    Yohann Lu

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, February 23, 2016 6:45 AM

All replies

  • User1814019480 posted

    hi rtang,

    I wonder know how do you parse your date string on server side?

    I guess you may need use the DateTimeOffset method or SpecifyKind method. The similar thread about this issue , please refer to it:

    http://stackoverflow.com/questions/3188933/prevent-timezone-conversion-on-deserialization-of-datetime-value

    Regards,

    Will

    Tuesday, February 9, 2016 10:49 AM
  • User1649945628 posted

    Hi will,

    I'm sending the data in JSON format. From the server side I'm not doing any manual de-serialization.  It's very hard for me to change all DateTime properties to DateTimeOffset at this stage. The controllers are derived from ODataController. When I sending the same Date Time data to POST or PUT,  it's de-serialized with different value. For example, the following two methods get different Date information even when the same data were sent:

    [System.Web.Http.HttpPost]
    public IHttpActionResult Post([FromBody] Category category)
    {
        //category.UpdateDate is correct;
    }

    [System.Web.Http.HttpPut]
    public IHttpActionResult Put([FromODataUri] int id, [FromBody] Category category)
    {
      //category.UpdateDate is incorrect;
    }

    Wednesday, February 10, 2016 9:01 PM
  • User36583972 posted

    Hi rtang2011,

    From your description, I suggest you can customize CustomJsonMediaTypeFormatter class. Through this you can correctly handle type, to ensure data consistency before and after.

    The following code for your reference.

    CustomJsonMediaTypeFormatter:

    public class CustomJsonMediaTypeFormatter : MediaTypeFormatter
        {
            public JsonSerializerSettings _jsonSerializerSettings;
            public CustomJsonMediaTypeFormatter()
            {
                _jsonSerializerSettings = CustomJsonSettings.Instance;
                // Fill out the mediatype and encoding we support
                SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
                SupportedEncodings.Add(new UTF8Encoding(false, true));
            }
            public override bool CanReadType(Type type)
            {
                return true;
            }
            public override bool CanWriteType(Type type)
            {
                return true;
            }
            public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
            {
                // Create a serializer
                Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(_jsonSerializerSettings);
                // Create task reading the content
                return Task.Factory.StartNew(() =>
                {
                    using (StreamReader streamReader = new StreamReader(stream, SupportedEncodings.First()))
                    {
                        using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
                        {
                            return serializer.Deserialize(jsonTextReader, type);
                        }
                    }
                });
            }
            public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
            {
                // Create a serializer
                Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(_jsonSerializerSettings);
                // Create task writing the serialized content
                return Task.Factory.StartNew(() =>
                {
                    using (StreamWriter streamWriter = new StreamWriter(stream, SupportedEncodings.First()))
                    {
                        using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter))
                        {
                            serializer.Serialize(jsonTextWriter, value);
                        }
                    }
                });
    
            }
    
        }
    
    
        public static class CustomJsonSettings
        {
            private static JsonSerializerSettings _settings;
            public static JsonSerializerSettings Instance
            {
                get
                {
                    if (_settings == null)
                    {
                        var settings = new JsonSerializerSettings();
                        // Must convert times coming from the client (always in UTC) to local - need both these parts:
                        JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
                        settings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal }); // Critical part 1
                        settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; // Critical part 2
                                                                                    // Skip circular references
                        settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                        // Handle special cases in json (self-referencing loops, etc)
                        //settings.ContractResolver = new CustomJsonResolver();
                        _settings = settings;
                    }
                    return _settings;
                }
            }
        }
    

    WebApiConfig:

    public static void Register(HttpConfiguration config)
            {
                GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
                GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
                GlobalConfiguration.Configuration.Formatters.Add(new CustomJsonMediaTypeFormatter());
    
                ODataModelBuilder builder = new ODataConventionModelBuilder();
                builder.EntitySet<Category>("Category");
                config.MapODataServiceRoute(
                    routeName: "ODataRoute",
                    routePrefix: "odata",
                    model: builder.GetEdmModel());
            }
    

    The tutorial about Using JSON.NET with ASP.NET Web API in the below:

    https://code.msdn.microsoft.com/Using-JSONNET-with-ASPNET-b2423706

    Best Regards,

    Yohann Lu

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, February 23, 2016 6:45 AM