Newtonsoft.Json
Newtonsoft.Json copied to clipboard
Deserialization of TimeOnly fails for "HH:mm" formatted times
Deserialization of TimeOnly
fails for the HH:mm
format - the seconds is required.
This leads to compatibility issues since HH:mm
is the default format returned by HTML <input type="time" />
The similar TimeSpan
properly deserializes this format.
Source/destination types
TimeOnly
Source/destination JSON
"17:05"
Expected behavior
The time is deserialized as 17:05
Actual behavior
Error converting value "17:05" to type 'System.TimeOnly'.
Steps to reproduce
JsonConvert.DeserializeObject<TimeOnly>("\"17:05\"")
I am seeing this issue when I try to retrieve "09:00" from Cosmos. Can this change be merged soon? The solution seems to reasonable.
+1
This is my temporary workaround while waiting for the issue to be fixed.
public class TimeOnlyConverter : JsonConverter
{
public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer)
{
try
{
bool result = TimeOnly.TryParse(reader?.Value?.ToString(), out TimeOnly time);
if (result)
{
return time;
}
}
catch { }
return Activator.CreateInstance(type);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue(((TimeOnly?)value)?.ToString("HH:mm", CultureInfo.InvariantCulture));
}
public override bool CanConvert(Type type)
{
return type == typeof(TimeOnly) || type == typeof(TimeOnly?);
}
}
.AddNewtonsoftJson(o =>
{
o.SerializerSettings.Converters.Add(new TimeOnlyConverter());
});
To add to @michelebenolli workarround: In my case, it was a TimeOnly property in an object I wanted to deserialize. This is also possible, you just need to add a ContractRevolver to change the behavior of the serializer/deserializer to use the custom converter instead of the normal one:
internal sealed class TimeOnlyContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization )
{
var property = base.CreateProperty( member, memberSerialization );
if( property.PropertyType == typeof( TimeOnly ) )
{
property.Converter = new TimeOnlyConverter();
}
return property;
}
}
Then use it like so:
YourObject Result = JsonConvert.DeserializeObject<YourObject>(yourJsonString, new JsonSerializerSettings {
ContractResolver = new TimeOnlyContractResolver()
});
It is a bit of a workaround so I hope that this change will be merged after all this time. However, it does make you learn about how serialization/deserialization works :).
Some sites I used for this information: https://stackoverflow.com/questions/45643903/anyway-to-get-jsonconvert-serializeobject-to-ignore-the-jsonconverter-attribute https://github.com/JamesNK/Newtonsoft.Json/issues/2056