【问题标题】:Json.NET TypeNameHandling and TypeNameAssemblyFormatHandlingJson.NET TypeNameHandling 和 TypeNameAssemblyFormatHandling
【发布时间】:2019-05-05 06:28:13
【问题描述】:

希望您能帮助我了解 Json.NET 中的 TypeNameHandlingTypeNameAssemblyFormatHandling 设置是如何工作的。

场景

假设有这些实体:

namespace SerDesTest
{
    public abstract class BaseClass
    {
        public int Int0 { get; set; }
        public string String0 { get; set; }
    }

    public class Father : BaseClass
    {
        public Father() { Children = new List<BaseClass>(); }

        public List<BaseClass> Children { get; set; }
    }

    public class Child : BaseClass
    {
        public Child(Father father) { Father = father; }

        public Father Father { get; set; }
        public int Int1 { get; set; }
        public string String1 { get; set; }
    }

    public class Generic<T>
    {
        public int GenericInt { get; set; }
        public string GenericString { get; set; }
        public T GenericProperty { get; set; }
    }
}

并使用以下辅助方法:

public static class JsonHelper
{
    public static string Serialize(object obj)
    {
        return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            SerializationBinder = new JsonManagerKnownTypesBinder()
            {
                KnownTypes = GetTypeList("SerDesTest")
            }
        });
    }

    public static TEntity Deserialize<TEntity>(string json)
    {
        return JsonConvert.DeserializeObject<TEntity>(json, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            SerializationBinder = new JsonManagerKnownTypesBinder()
            {
                KnownTypes = GetTypeList("SerDesTest")
            }
        });
    }

    public static IList<Type> GetTypeList(string assemblyName)
    {
        List<Type> list = new List<Type>();
        Assembly assembly = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{assemblyName}.dll"));

        if (assembly != null)
        {
            list.AddRange(assembly.GetTypes());
        }
        return list.AsParallel().Distinct().ToList<Type>();
    }
}

运行如下:

internal static class Program
{
    private static void Main(string[] args)
    {
        try
        {
            Father father = new Father();
            Generic<Child> generic = new Generic<Child>
            {
                GenericInt = 100,
                GenericString = "100",
                GenericProperty = new Child(father)
                {
                    Int0 = 0,
                    Int1 = 1,
                    String0 = "0",
                    String1 = "1"
                }
            };

            string genericJson = JsonHelper.Serialize(generic);

            Console.WriteLine(genericJson);

            Generic<Child> genericDes = JsonHelper.Deserialize<Generic<Child>>(genericJson);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.ReadKey();
    }
}

运行测试程序

运行测试程序时,我得到如下json:

{
  "$type": "Generic`1",
  "GenericInt": 100,
  "GenericString": "100",
  "GenericProperty": {
    "$type": "Child",
    "Father": {
      "$type": "Father",
      "Children": [],
      "Int0": 0,
      "String0": null
    },
    "Int1": 1,
    "String1": "1",
    "Int0": 0,
    "String0": "0"
  }
}

还有以下例外:

JSON 中指定的类型 'SerDesTest.Generic1, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with 'SerDesTest.Generic1[[SerDesTest.Child, SerDesTest, 版本=1.0.0.0,文化=中性,PublicKeyToken=null]],SerDesTest, 版本=1.0.0.0,文化=中性,PublicKeyToken=null'。路径'$类型', 第 2 行,第 22 位。

问题

我有两个问题/问题:

1) 由于 Json.NET 配置,我在 json 的第 2 行中的期望是具有 $type 字段的“全限定名”,例如:"$type": "SerDesTest. Generic[SerDesTest,CHild]" 而不仅仅是 "$type": "Generic`1"...

2) 你对解决异常有什么想法吗?

谢谢, 阿提利奥

更新 1

按照 Brian Rogers 的要求,按照JsonManagerKnownTypesBinder 的代码:

public class JsonManagerKnownTypesBinder : ISerializationBinder
{
    public IList<Type> KnownTypes { get; set; }

    public Type BindToType(string assemblyName, string typeName)
    {
        return KnownTypes.SingleOrDefault(t => t.Name == typeName);
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = null;
        typeName = serializedType.Name;
    }
}

更新 2

更多测试。如果我不设置SerializationBinder 我使用这个版本的JsonHelper 类:

public static class JsonHelper
{
    public static string Serialize(object obj)
    {
        return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            //SerializationBinder = new JsonManagerKnownTypesBinder()
            //{
            //    KnownTypes = GetTypeList("SerDesTest")
            //}
        });
    }

    public static TEntity Deserialize<TEntity>(string json)
    {
        return JsonConvert.DeserializeObject<TEntity>(json, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            //SerializationBinder = new JsonManagerKnownTypesBinder()
            //{
            //    KnownTypes = GetTypeList("SerDesTest")
            //}
        });
    }
}

我解决了我所有的问题:首先我在反序列化过程中没有例外;其次,我有以下 json 作为输出:

{
  "$type": "SerDesTest.Generic`1[[SerDesTest.Child, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
  "GenericInt": 100,
  "GenericString": "100",
  "GenericProperty": {
    "$type": "SerDesTest.Child, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
    "Father": {
      "$type": "SerDesTest.Father, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
      "Children": {
        "$type": "System.Collections.Generic.List`1[[SerDesTest.BaseClass, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e",
        "$values": []
      },
      "Int0": 0,
      "String0": null
    },
    "Int1": 1,
    "String1": "1",
    "Int0": 0,
    "String0": "0"
  }
}

您可以使用TypeNameAssemblyFormatHandling 选项来调整$Type 字段中包含的信息。

那么问题就变成了:如何实现ISerializationBinder 接口以获得更安全的软件?

【问题讨论】:

  • @BrianRogers 完成。谢谢
  • GetTypeList 返回您的 SerDesTest 类的开放类型定义(未指定类型),而您的 GetTypeList 除外一个关闭类型定义(如错误状态):SerDesTest&lt;SerDesTest.Child&gt;

标签: c# json serialization json.net


【解决方案1】:

要为 $type 字段设置“全限定名”,请在 Update 1 中更改 JsonManagerKnownTypesBinder 以在 BindToTypeBindToName 方法中设置程序集名称,如下所示

public class JsonManagerKnownTypesBinder : ISerializationBinder
{
    public IList<Type> KnownTypes { get; set; }

    public Type BindToType(string assemblyName, string typeName)
    {
        return KnownTypes.SingleOrDefault(t => t.Name == typeName && t.Assembly.GetName().Name == assemblyName);
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = serializedType.Assembly.GetName().Name;
        typeName = serializedType.FullName;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多