Runtime: JsonSerializer is unable to deserialize a valid JSON

Created on 15 Jun 2019  路  15Comments  路  Source: dotnet/runtime

Given the following payload:

{
    "picture": "http://placehold.it/32x32", 
    "eyeColor": "Brown",    
    "registered": "2015-05-30T01:50:21 -01:00"  
}

And the following models:

public sealed class Model
{
    public Color EyeColor { get; set; }
    public Uri Picture { get; set; }
    public DateTime Registered { get; set; }
}

public enum Color
{
    Blue,
    Green,
    Brown
}

An exception is thrown when attempting to deserialize using:

JsonSerializer.Parse<Model>(
    json, 
    new JsonSerializerOptions
    {
        IgnoreNullValues = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = false
    });

Exception:

System.Text.Json.JsonException: 'The JSON value could not be converted to System.Uri. Path: $.picture | LineNumber: 1 | BytePositionInLine: 39.'

In fact, it is unable to deserialize any of the three properties and not just the Uri.

This is running against the following:

Version: 3.0.0-preview6-27804-01
Commit:  fdf81c6faf

The same payload is deserialized as expected in both UTF8Json and JSON.NET.

area-System.Text.Json enhancement

Most helpful comment

@NimaAra I've added a scenario test example at https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/tests/Serialization/ReadScenarioTests.cs#L17 that you can reference on how to do this.

Uri works now out of the box. You can plug in the built-in "JsonStringEnumConverter" to get the enum as string parsing. For DateTime you can make a more lenient converter pretty easily. All of this is shown in the link.

All 15 comments

if you change picture to a string does it work? Support for Uri is currently missing https://github.com/dotnet/corefx/issues/38536

String works but both other properties also fail.

Enum is not resolved and datetime also fails.

The Datetime has a whitespace before the Timezone. Last Time I checked that's not allowed.

Also enums are integer per default (afaik), so that would explain the enums.

Sure, but is there a plan to provide means to be able to support these? Other libraries can handle the payload.

The Datetime is imho an invalid value.
You can enable String enums via a setting in JsonValueConverterEnum, but I don't know how to configure that in general. See https://github.com/dotnet/corefx/blob/717217d664d29dc4621aba97e6870996c33460cd/src/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterEnum.cs#L19 (treatAsString)

The string support for enums is a known limitation and we are are working to fix that for 3.0 (will be done via a custom built-in converter, once we have finalized the shape/design of converters).
https://github.com/dotnet/corefx/issues/36639

cc @steveharter, @JeremyKuhne

I'll update this issue with more details once we have that resolved.

The Datetime is imho an invalid value.

Yep. I don't think we would resolve this particular issue (around DateTime). If it is critical to your scenario, a custom converter would need to be implemented.

Colors is a struct without settable properties, and the JsonSerializer primarily supports simple POCOs (public classes with get/set properties), for v1. Our plan is to continue to add support to enable such cases, driven by how commonly they occur. Adding support for structs like these would go in that bucket (similar to adding field support). cc @YohDeadfall

And as mentioned previously, we don't have Uri support atm. I think Uri will occur quite a bit, so we will try to squeeze that in for 3.0.

@ahsonkhan I am not sure why you brought up Colors as in my example I am trying to resolve Color which is a custom _enum_.

FYI, the sample _JSON_ was generated by: JsonGenerator which regards that value for _DateTime_ as standard.

I am not sure why you brought up Colors as in my example I am trying to resolve Color which is a custom _enum_.

Sorry, I missed the part that Color is a custom enum that you defined, and confused it with the built-in System.Drawing.Color type:
https://github.com/dotnet/corefx/blob/dc9a0f2c5c3e1718a75a9ed508e8a06309c174fb/src/System.Drawing.Primitives/src/System/Drawing/Color.cs#L15

No problem, thanks for your work on this. Looking forward to the additional features closing _some_ of the gaps with other widely used libraries.

I think the date-time in top post is valid and default parser understands it well:

System.DateTime.Parse("2015-05-30T01:50:21 -01:00") returns 5/30/2015 2:50:21 AM

However, STJ (from today's nightly build) throws the following exception for date-time:

Unhandled exception. System.Text.Json.JsonException: The JSON value could not be converted to System.DateTime. Path: $.registered | LineNumber: 4 | BytePositionInLine: 46.
   at System.Text.Json.ThrowHelper.ThowJsonException(String message, Utf8JsonReader& reader, String path)
   at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType, Utf8JsonReader& reader, String path)
   at System.Text.Json.JsonPropertyInfoNotNullable`3.Read(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.JsonSerializer.HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
   at System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, Utf8JsonReader& reader)
   at System.Text.Json.JsonSerializer.ParseCore(String json, Type returnType, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Parse[TValue](String json, JsonSerializerOptions options)
   at Program.Main() in C:\projects\test\corefx\gh-38568\Program.cs:line 40

Also, the payload in author's example de-serializes well with Json.NET's JsonConvert.DeserializeObject<T> API without customization: https://dotnetfiddle.net/vFQRpv.

@NimaAra I've added a scenario test example at https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/tests/Serialization/ReadScenarioTests.cs#L17 that you can reference on how to do this.

Uri works now out of the box. You can plug in the built-in "JsonStringEnumConverter" to get the enum as string parsing. For DateTime you can make a more lenient converter pretty easily. All of this is shown in the link.

Thanks @JeremyKuhne

@JeremyKuhne If you're pointing people to your example, I think that need to be fixed to something like

public class DateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => DateTime.Parse(reader.GetString(), null, System.Globalization.DateTimeStyles.RoundtripKind);

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
        => writer.WriteStringValue(value.ToString("o"));
}

for it to preserve the timezone into across round-trips. The Read is different from your sample where we were losing time equal to the difference between utc and the timezone of the source timestamp.

And as a followup, is there plans to officially support more DateTime strings than the current strict one, requiring these extensions? At the minimum, one would expect matching Newtonsoft since with 3.0 more folks with be migrating over from that scenario. Same with enum support in-built by default (vs currently having special config to adding JsonStringEnumConverter to JsonSerializerOptions's Converters list)

If you're pointing people to your example, I think that need to be fixed to something like

Ideally you shouldn't write your own unless you have to. The built-in one is ISO compliant and won't lose your data. @tarekgh can speak more to best practices for DateTime. The example was only meant to test and demonstrate the mechanics of customizing.

is there plans to officially support more DateTime strings than the current strict one, requiring these extensions?

Nothing specific that I'm aware of, but that doesn't mean we won't. @steveharter or @ahsonkhan can speak more to design considerations and future plans.

Same with enum support in-built by default (vs currently having special config to adding JsonStringEnumConverter to JsonSerializerOptions's Converters list)

Json.NET does the same thing, unless there is some other aspect that I'm not aware of. We have built-in enum conversion, you just have to specify the converter if you want string conversion- as you do with StringEnumConverter.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Timovzl picture Timovzl  路  3Comments

jamesqo picture jamesqo  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

omajid picture omajid  路  3Comments

v0l picture v0l  路  3Comments