在创建 JsonObjectContract 时禁止$type

本文关键字:type 禁止 创建 JsonObjectContract | 更新日期: 2023-09-27 18:35:20

我试图强制抑制一些特定的$type输出(同时保留其他输出)Json.NET。它们在我的应用程序中是不必要的。Json.NET 源代码显示了这一点:

private bool ShouldWriteType(...)
{
  TypeNameHandling resolvedTypeNameHandling =
    ((member != null) ? member.TypeNameHandling : null)
    ?? ((containerProperty != null) ? containerProperty.ItemTypeNameHandling : null)
    ?? ((containerContract != null) ? containerContract.ItemTypeNameHandling : null)
    ?? Serializer._typeNameHandling;

这让我认为我可以通过在容器协定中设置ItemTypeNameHandling来覆盖序列化程序设置。唉,它不适用于数组。我尝试覆盖该方法以获取数组协定,但那里的设置会影响数组中的每个项目。那不是我想要的。(SignalR 发送对象数组中的所有方法参数。这是我的完整演示代码,我试图弄清楚如何在输出中保留A类型,但抑制B类型(以及所有sealed类)。如何覆盖合约解析程序以正确执行此操作?

using System;
using System.Diagnostics;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace DemoBadTypeInclusion
{
    class A { public int P1 { get; set; } }
    // this attribute doesn't work and I don't want to have to reference Json.NET in my POCO
    //[JsonObject(ItemTypeNameHandling = TypeNameHandling.None)]
    sealed class B { public int P2 { get; set; } }
    internal class DtoContractResolver : DefaultContractResolver
    {
        protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            var contract = base.CreateObjectContract(objectType);
            if (objectType.IsSealed && objectType.BaseType == typeof(object))
            {
                contract.ItemTypeNameHandling = TypeNameHandling.None;
            }
            return contract;
        }
    }
    class Program
    {
        static void Main()
        {
            var objs = new object[] {new A {P1 = 3}, new B {P2 = 4}};
            var settings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
                ContractResolver = new DtoContractResolver(),
            };
            var json = JsonConvert.SerializeObject(objs, settings);
            Console.WriteLine(json);
            if (Debugger.IsAttached)
                Console.ReadKey();
        }
    }
}

更新:在研究了 Json.NET 代码之后,似乎SerializeList的这一行是我麻烦的根源:

JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);

我想要回退值(GetContractSafe)。有谁知道如何使FinalItemContract为空?

在创建 JsonObjectContract 时禁止$type

如果禁止对象 Array 的一个类型引用,则会使孔 Array 不可反序列化。因此,您可以禁止数组的所有类型引用。

仅禁止对象数组的类型引用

protected override JsonArrayContract CreateArrayContract(Type objectType)
{
            var c = base.CreateArrayContract(objectType);
            if(objectType == typeof(Object[]))
                c.ItemTypeNameHandling = TypeNameHandling.None;
            return c;
}

如果不需要反序列化回原始对象,只需设置

new JsonSerializerSettings
{
  TypeNameHandling = TypeNameHandling.None,
  ...
};

我知道这不是一个确切的答案,但我认为没有用用于标识对象数组的某些元素的组件,这些元素具有要反序列化的类型。所以我认为,这就是为什么它不受 json.net 支持的原因

毕竟这里有一种解决方法,它只抑制一种类型:

static void Main()
        {
            var objs = new object[] { new A { P1 = 3 }, new B { P2 = 4 } };
            var settings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Objects,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
                ContractResolver = new DtoContractResolver(),
            };

            var serial = JsonSerializer.Create(settings);
            var jsonArray = new JArray();

            foreach (var obj in objs)
            {
                if (obj != null && obj.GetType().IsSealed && obj.GetType().BaseType == typeof(object))
                    serial.TypeNameHandling = TypeNameHandling.None;
                else
                    serial.TypeNameHandling = TypeNameHandling.Objects;
                jsonArray.Add(JObject.FromObject(obj, serial));
            }
            var json = jsonArray.ToString(Formatting.None);
            Console.WriteLine(json);
            if (Debugger.IsAttached)
                Console.ReadKey();
        }