JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.64k stars 3.24k forks source link

Opt-out of JTokenType.Guid (and other "custom" JTokenTypes) #2948

Open mvarendorff opened 2 months ago

mvarendorff commented 2 months ago

Source/destination types

public record Example(Guid Id);

Source/destination JSON

{ "Id": "e4729b1e-3f1f-4386-b5c7-2a828280f8f0" }

Expected behavior

JObject.FromObject(x) acts as a "shortcut" to JObject.Parse(JsonConvert.SerializeObject(x)). If it doesn't act like such a shortcut by default, a flag could be passed to the serializer for JObject.FromObject to enable behaviour that makes it a shortcut to serializing to string, then parsing again.

Actual behavior

JObject.FromObject embeds additional metadata into the values, like JTokenType.Guid, JTokenType.Date, etc. This creates a difference in behaviour between JObject.FromObject and using JObject.Parse on a previously serialized version of the same object. This is very frustrating when having to write code that has to deal with JObject instances from both sources.

Steps to reproduce

public record Example(Guid Id);

public class Program {
    public static void Main(string[] args) {
        var example = new Example(Guid.NewGuid());

        // Convert to string and parse that
        var exampleJsonFromParsedString = JObject.Parse(JsonConvert.SerializeObject(example));
        var exampleIdFromParsedString = exampleJsonFromParsedString.Value<string>("Id");
        Console.Out.WriteLine("exampleIdFromParsedString = {0}", exampleIdFromParsedString);

        // Convert to JObject directly
        var exampleJsonFromObject = JObject.FromObject(example);
        var exampleIdFromObject = exampleJsonFromObject.Value<string>("Id");
        Console.Out.WriteLine("exampleIdFromObject = {0}", exampleIdFromObject);
    }
}

Related: https://github.com/JamesNK/Newtonsoft.Json/issues/1449 (note, I am not saying those JTokenTypes should be removed, I am just asking for a way to opt-out of them when serializing directly to JObject).

alhimik45 commented 1 month ago

Agree, using JToken.DeepEquals becomes hard when you have to compare values from Parse and FromObject