Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Text.Json fails to deserialize objects with a Guid #31465

Closed
RasmusWesterlundh opened this issue Nov 11, 2019 · 7 comments
Closed

System.Text.Json fails to deserialize objects with a Guid #31465

RasmusWesterlundh opened this issue Nov 11, 2019 · 7 comments

Comments

@RasmusWesterlundh
Copy link

Description

Deserializing an object containing a Guid will result in a null value, even if the json string and Guid is correctly formatted.

Deserializing the same object using Newtonsoft.Json is successful and gives the expected object.

Example code to reproduce

//SystemApplication is an object containing a Guid-property and several string-properties

var response = await httpClient.GetAsync($"v1/some/endpoint");
var content =  await response.Content.ReadAsStringAsync();
var result_SystemText = System.Text.Json.JsonSerializer.Deserialize<SystemApplication>(content); //Will result in null-object
var result_Newtonsoft = Newtonsoft.Json.JsonConvert.DeserializeObject<SystemApplication(content); //Will result in correctly serialized object

dotnet --info

PS C:\windows\system32> dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.0.100
Commit: 04339c3a26

Runtime Environment:
OS Name: Windows
OS Version: 10.0.17763
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100\

Host (useful for support):
Version: 3.0.0
Commit: 7d57652f33

.NET Core SDKs installed:
2.1.802 [C:\Program Files\dotnet\sdk]
2.2.402 [C:\Program Files\dotnet\sdk]
3.0.100 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

@layomia
Copy link
Contributor

layomia commented Nov 11, 2019

@RasmusWesterlundh can you provide a snippet of your content payload (particularly the showing the Guid format (see remarks section)), and if possible your SystemApplication class schema?

@RasmusWesterlundh
Copy link
Author

RasmusWesterlundh commented Nov 11, 2019

Hi @layomia,

The only content-payload I have at the moment is with sensitive system information from production data, so I cannot share that with you. I hope this should be fairly easy to replicate none-the-less.

In the content-payload the Guid is 32 digits separated with hyphens ("D"-specifier).

In the successful deserialization by Newtonsoft.Json that same value is however noted as 32-digits separated by hyphens, enclosed in bracers ("B"-specifier).

@ahsonkhan
Copy link
Member

ahsonkhan commented Nov 12, 2019

@RasmusWesterlundh - can you provide a simplified/anonymized repro of your issue by extracting that out from your specific application (removing/replacing any sensitive info)?

Does your class schema contain public properties with getters and setters? Only those are supported.
Is there a property name mismatch in the payload vs your class schema (maybe casing difference)?

Does your JSON payload contain the Guid in the "B" format, i.e. surrounded by braces? The JsonSerializer by default uses the "D" format for parsing, and hence would fail for other formats. If you need to support custom formats, you'd need to write your own Guid converter (similar to when you need to support different date time formats: https://docs.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support

This works as expected:

[Fact]
public static void GuidDeserializationTest()
{
    Foo f = JsonSerializer.Deserialize<Foo>("{\"MyGuid\":\"090bbc52-bfe3-4fa3-9b36-25d7e624e662\"}");
    Guid expected = new Guid("090bbc52-bfe3-4fa3-9b36-25d7e624e662");
    Assert.Equal(expected, f.MyGuid);
}

public class Foo
{
    public Guid MyGuid { get; set; }
}

@RasmusWesterlundh
Copy link
Author

RasmusWesterlundh commented Nov 12, 2019

The Guid is in "D"-format in the json payload.
No naming mismatches, I doubt I that the Newtonsoft deserializer would have managed to deserialize if that had been the case.

Edit: I'm sorry, my previous testing of this must have been very incomplete. It is not related to the Guid at all it seems. Removing the Guid property from the class object and removing it from the content string still results in the System.Text.JsonSerializer to fail the deserialization.
Newtonsoft still manages to deserialize.
I just got the feeling that this maybe belongs to StackOverflow instead 😅

string content = "{\"data\":{\"Type\":\"Foo\",\"Description\":\"bar \",\"Software\":\"baz\",\"Version\":\"qux\",\"Url\":\"http://example.com\",\"DatabasePortNumber\":1,\"Id\":\"2cdc66f1-0000-0000-39ac-233c00000000\"}}";

var result_SystemText = System.Text.Json.JsonSerializer.Deserialize<SystemApplicationResponse>(content);  //Will result in null-object
var result_Newtonsoft = Newtonsoft.Json.JsonConvert.DeserializeObject<SystemApplicationResponse>(content); //Will result in correctly serialized object
    public class SystemApplicationResponse
    {
        public SystemApplicationDto Data { get; set; }
    }


    public class SystemApplicationDto
    {
        public SystemApplicationDtoType Type { get; set; }

        public string Description { get; set; }

        public string Software { get; set; }

        public string Version { get; set; }
        public string Url { get; set; }

        public int DatabasePortNumber { get; set; }
        public System.Guid Id { get; set; }
    }

    public enum SystemApplicationDtoType
    {
        Foo = 0
    }

@khellang
Copy link
Member

No naming mismatches, I doubt I that the Newtonsoft deserializer would have managed to deserialize if that had been the case.

The very first property (data) in your JSON doesn't match the property name (Data) 🙈

@RasmusWesterlundh
Copy link
Author

@khellang
You are correct. Changing it to an uppercase results in System.Text throwing an error on trying to deserialize the SystemApplicationDtoType.

But at this point it is very apparent that this belongs to StackOverflow rather than as a github issue.... 🙃

@ahsonkhan
Copy link
Member

ahsonkhan commented Nov 12, 2019

Changing it to an uppercase results in System.Text throwing an error on trying to deserialize the SystemApplicationDtoType.

System.Text.Json.JsonException : The JSON value could not be converted to System.Text.Json.Serialization.Tests.ReadValueTests+SystemApplicationDtoType. Path: $.Data.Type | LineNumber: 0 | BytePositionInLine: 21.

That's likely because you are deserializing an enum with string values (the default only supports integer values for enums). Register the JsonStringEnumConverter as part of JsonSerializerOptions to get the expected behavior.

var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());

var result_SystemText = JsonSerializer.Deserialize<SystemApplicationResponse>(content, options);

Edit: I'm sorry, my previous testing of this must have been very incomplete. It is not related to the Guid at all it seems. Removing the Guid property from the class object and removing it from the content string still results in the System.Text.JsonSerializer to fail the deserialization.

I suspected that.

But at this point it is very apparent that this belongs to StackOverflow rather than as a github issue....

No problem. I am glad the issue got resolved :)

@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants