JsonKnownTypes icon indicating copy to clipboard operation
JsonKnownTypes copied to clipboard

Serialize with known types

Open KarlGong opened this issue 4 years ago • 1 comments

using System;
using JsonKnownTypes;
using Newtonsoft.Json;

namespace Test 
{
    [JsonConverter(typeof(JsonKnownTypesConverter<BaseClass>))]
    public class BaseClass 
    {
        public string Name {get;set;}
    }

    public class ChildClass:BaseClass 
    {
        public string Detailed {get;set;}
    }

    public class MyClass
    {
        public ChildClass ChildClass {get;set;}
    }

    public class Program
    {
        public static void Main()
        {
            var myClass = new MyClass()
            {
                ChildClass = new ChildClass()
            };

            Console.WriteLine(JsonConvert.SerializeObject(myClass)); 
            // output: {"ChildClass":{"$type":"ChildClass","Detailed":null,"Name":null}}
            // but I think the $type should not be output, because the type is known.

            JsonConvert.DeserializeObject<MyClass>("{\"ChildClass\":{\"$type\":\"ChildClass\",\"Detailed\":null,\"Name\":null}}"); 
            // work

            JsonConvert.DeserializeObject<MyClass>("{\"ChildClass\":{\"Detailed\":null,\"Name\":null}}"); 
            // doesn't work
            // it should work, because the type is known.
        }
    }
}

When I serialize MyClass, I think the $type of ChildClass should not be output, because the type is known. Vice versa, when I deserialize MyClass, I do not need to provide the $type.

You can refer https://dotnetfiddle.net/Mk0rkO for detail.

KarlGong avatar May 12 '21 02:05 KarlGong

@KarlGong Hi, sorry for not replying for a long time. Use UseBaseTypeForCanConvert in JsonKnownTypesSettingsManager.DefaultDiscriminatorSettings or JsonDiscriminatorAttribute. Ive added the flag because i don't want to change current behaviour but your point is good. Thanks for help. Will release it soon

dmitry-bym avatar Apr 12 '22 04:04 dmitry-bym

Dmitry, I am unable to get this scenario to work using the latest version available through Github.

On further reading, I found that Newtonsoft does not call CanConvert when the Converter is directly specified. Do you think this is reasonable to work around? https://stackoverflow.com/a/26018381

A repro:

[TestClass]
public class KnownTypesDeserializeAsBaseErrorRepro
{
    public enum ThingType
    {
		Base,
		Derived,
    }
    [JsonConverter(typeof(JsonKnownTypesConverter<Base>))]
    [JsonKnownTypeFallback(typeof(Base))]
    [JsonDiscriminator(Name = nameof(Base.Type))]
    public abstract class Base
    {
        public string BaseProp { get; set; }
		public abstract ThingType Type { get; }
    }

    public class Derived : Base
    {
        public string DerivedProp { get; set; }

        public override ThingType Type => ThingType.Derived;
    }

    [TestMethod]
    public void TestDerivedTypeDeserialization()
    {
        //in the scenario where an endpoint accepts a derived type, and the client sends to type discriminator,
        //everything should succeed

        JsonKnownTypesSettingsManager.DefaultDiscriminatorSettings = new JsonDiscriminatorSettings { UseBaseTypeForCanConvert = false };

        var @base = new Derived() { BaseProp = "base" };

        var json = JsonConvert.SerializeObject(@base, Formatting.Indented);

        Console.WriteLine(json);

        //remove the $type discriminator to simulate the scenario
        var token = JToken.Parse(json);
        token["Type"].Parent.Remove();

        Console.WriteLine(token.ToString(Formatting.Indented));

        var deserialized = JsonConvert.DeserializeObject<Derived>(token.ToString());
    }
}

jonmotos avatar May 02 '24 14:05 jonmotos