7

I am having an issue deserializing time values into LocalTime- granted, I am pretty new to NodaTime. I want to import a web service result which lists a time in "HH:mm" format. I get an exception unless I use a time in "hh:mm:ss.fff" format. Is there a way to specify a different pattern and get "HH:mm" to work?

Please see this code

using System;
using System.Collections.Generic;

using Newtonsoft.Json;
using NodaTime;
using NodaTime.Serialization.JsonNet;
using NodaTime.Text;    // Required for LocalTimePattern
namespace TestNodaTime
{
    class MyObject
    {
        [JsonProperty("var1")]
        public int MyProperty { get; set; }

        [JsonProperty("time")]
        public LocalTime MyTime { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string serializedObject1 = "[{\"var1\":\"42\",\"time\":\"01:02:03.004\"}]";
            string serializedObject2 = "[{\"var1\":\"42\",\"time\":\"01:02\"}]";

            JsonSerializerSettings jss = new JsonSerializerSettings();
            jss.ConfigureForNodaTime(DateTimeZoneProviders.Bcl);

            // This works - the pattern is "hh:mm:ss.fff"
            MyObject mo1 = JsonConvert.DeserializeObject<List<MyObject>>(serializedObject1, jss)[0];

            // This causes an exception  - the pattern is "HH:mm"
            MyObject mo2 = JsonConvert.DeserializeObject<List<MyObject>>(serializedObject2, jss)[0];
            /*
             * An unhandled exception of type 'NodaTime.Text.UnparsableValueException' occurred in Newtonsoft.Json.dll
             * Additional information: The value string does not match a quoted string in the pattern. 
             * Value being parsed: '01:02^'. (^ indicates error position.)
             */
        }
    }
}

Exception being thrown:

NodaTime.Text.UnparsableValueException was unhandled
  HResult=-2146233033
  Message=The value string does not match a quoted string in the pattern. Value being parsed: '01:02^'. (^ indicates error position.)
  Source=NodaTime
  StackTrace:
       at NodaTime.Text.ParseResult`1.GetValueOrThrow()
       at NodaTime.Text.ParseResult`1.get_Value()
       at NodaTime.Serialization.JsonNet.NodaPatternConverter`1.ReadJsonImpl(JsonReader reader, JsonSerializer serializer)
       at NodaTime.Serialization.JsonNet.NodaConverterBase`1.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
       at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
       at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
       at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
       at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
       at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
       at TestNodaTime.Program.Main(String[] args)
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
David Rogers
  • 4,010
  • 3
  • 29
  • 28

1 Answers1

8

You'll need to remove the default converters it adds when calling ConfigureForNodaTime. Those converters use default formats and are not configurable (as far as I can tell).

JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ConfigureForNodaTime(DateTimeZoneProviders.Bcl);

jss.Converters.Remove(NodaConverters.LocalTimeConverter);
jss.Converters.Add(new NodaPatternConverter<LocalTime>(LocalTimePattern.CreateWithInvariantCulture("HH':'mm")));

MyObject mo2 = JsonConvert.DeserializeObject<List<MyObject>>(serializedObject2, jss)[0];
Rob
  • 26,989
  • 16
  • 82
  • 98
  • 1
    I had to add a: "using NodaTime.Text". Otherwise, perfect. Thanks! – David Rogers May 26 '16 at 04:33
  • @DavidRogers I've rolled back the edit due to a *slight* difference in the code. From the [documentation](http://nodatime.org/1.2.x/userguide/text.html) - *Where valid, : always refers to the culture-specific time separator (a colon in the invariant culture)* - This means that running the code in a local where `:` was *not* the time separator, it would fail to parse. Specifying `':'` means we're looking for the colon, regardless of locale. – Rob May 26 '16 at 23:19
  • It also keeps consistency with the default patterns: https://github.com/nodatime/nodatime/blob/69a2cdad9cb4c32a82620eae4f2460ff9479570a/src/NodaTime/Text/LocalDateTimePattern.cs#L76 – Rob May 26 '16 at 23:22
  • 1
    Thanks for the cleanup and for the links! I thought those apostrophes were causing me issues but I retested and obviously something else was going on. – David Rogers May 27 '16 at 21:44