C# 使用Json.NET禁用特定类型的类型注释
在远离C# 使用Json.NET禁用特定类型的类型注释,c#,json.net,C#,Json.net,在远离$type注释的过程中(为了使数据语言独立),我在理解各种typenameholling注释和类型契约的优先级时遇到了一些问题 在传输过程中,新类型和旧类型都将包含在相同的文件中。因此,除了新类型(ISettingsimplementation)之外,该文件必须包含所有类型的类型注释 我尝试用以下最低限度的例子再现我的问题: using System; using System.Collections.Generic; using Newtonsoft.Json; using System
$type
注释的过程中(为了使数据语言独立),我在理解各种typenameholling
注释和类型契约的优先级时遇到了一些问题
在传输过程中,新类型和旧类型都将包含在相同的文件中。因此,除了新类型(ISettings
implementation)之外,该文件必须包含所有类型的类型注释
我尝试用以下最低限度的例子再现我的问题:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Linq;
using Newtonsoft.Json.Serialization;
namespace jsontest
{
// I can't touch this class, even to add annotations
// (in practice there are too many classes to update)
interface IDoNotTouchThis {}
class DoNotTouchThisClass : IDoNotTouchThis {
public IMustHaveType MustHaveType => new HasType();
}
// This is the old data. It must have type annotations to be deserialized.
interface IMustHaveType {}
class HasType : IMustHaveType {}
// This is the new data. It must not have type annotations.
// There is a `JsonConverter` to figure out the types according to the fields.
interface ISettings {}
class SettingsFoo: ISettings {
public String Foo => "NotImportant";
}
class SettingsBar: ISettings {
public String Bar => "NotImportant";
public ISettings SubSettings => new SettingsFoo();
}
// This is the top-level class of the data.
class AllSettings {
public IDoNotTouchThis MustHaveType => new DoNotTouchThisClass();
public ISettings MustNotHaveType => new SettingsFoo();
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.None)] // This helps, but isn't enough
public IReadOnlyList<ISettings> MustNotHaveTypeEither => new List<ISettings> {
new SettingsFoo(),
new SettingsBar(),
};
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Program.Serialize(new AllSettings()));
}
private static JsonSerializerSettings SerializeSettings { get; }
= new JsonSerializerSettings()
{
// For backward compatibility:
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ContractResolver = new JsonConverterContractResolver(),
};
private static string Serialize<T>(T o)
{
return JsonConvert.SerializeObject(
o,
Formatting.Indented,
Program.SerializeSettings);
}
}
public class JsonConverterContractResolver : DefaultContractResolver
{
/// <inheritdoc />
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
// Here I'm hopping to disable type annotations for all `ISettings` instances.
if (objectType == typeof(ISettings)
|| (objectType.IsClass && objectType.GetInterfaces().Any(i => i == typeof(ISettings)))
|| (objectType == typeof(IReadOnlyList<ISettings>))
|| (objectType == typeof(List<ISettings>)))
{
if (contract is JsonContainerContract objectContract)
{
objectContract.ItemTypeNameHandling = TypeNameHandling.None;
}
}
return contract;
}
}
}
而不是
{
//没有必要,但不会造成伤害:
“$type”:“jsontest.AllSettings,pzyc3cmg.exe,版本=0.0.0.0,区域性=中性,PublicKeyToken=null”,
“MustHaveType”:{
“$type”:“jsontest.DoNotTouchThisClass,pzyc3cmg.exe,版本=0.0.0.0,区域性=neutral,PublicKeyToken=null”,
“MustHaveType”:{
$type:“jsontest.HasType,pzyc3cmg.exe,版本=0.0.0.0,区域性=中性,PublicKeyToken=null”
}
},
“MustNotHaveType”:{
“Foo”:“不重要”
},
“不得使用任何一种类型”:[
{
“Foo”:“不重要”
},
{
“酒吧”:“不重要”,
“子类别”:{
“Foo”:“不重要”
}
}
]
}
我尝试在不同的位置应用
TypeNameHandling
/ItemTypeNameHandling
注释,但没有成功。我正在寻找的Json.NET格式可能吗?奇怪的是,如果我打开它的头部并在默认情况下禁用类型序列化,然后在特定(旧)类型上启用它,我只能让任何东西工作。在您的情况下,这并不理想,因为这意味着您需要在解析器中列出所有旧类型,我想这是一项艰巨的工作
private static JsonSerializerSettings SerializeSettings { get; }
= new JsonSerializerSettings()
{
// Disable by default
TypeNameHandling = TypeNameHandling.None,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ContractResolver = new JsonConverterContractResolver(),
};
public class JsonConverterContractResolver : DefaultContractResolver
{
/// <inheritdoc />
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
if (contract is JsonContainerContract objectContract)
{
// enable on old types
if (typeof(IDoNotTouchThis).IsAssignableFrom(objectType))
{
objectContract.ItemTypeNameHandling = TypeNameHandling.All;
}
}
return contract;
}
}
小提琴:
更新: 我在
CreateContract
方法中找到了一种在JsonObjectContract
属性上动态设置typenameholling
的方法,似乎解决了这个问题。请查看.Net fiddler:
结果如下:
{
"$type": "jsontest.AllSettings, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.DoNotTouchThisClass, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.HasType, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
},
"MustNotHaveType": {
"Foo": "NotImportant"
},
"MustNotHaveTypeEither": [
{
"Foo": "NotImportant"
},
{
"Bar": "NotImportant",
"SubSettings": {
"Foo": "NotImportant"
}
}
]
}
“列出所有的旧类型”是绝对不可行的,所以我想我可以绕过这一点。但是,第一个
MustNotHaveType
有一个类型注释,即使它可以分配给ISettings
。似乎ItemTypeNameHandling
不适用于对象,而是适用于父对象的所有名称/值对(Json.NET似乎调用item)。不幸的是,这个解决方案非常脆弱。这将需要我在许多地方添加注释,并在合同中添加设置所属的所有可能类型。这是不正确的:。啊,我明白了,让我看看你还有什么可以利用的。但将属性添加到属性或类会对您有用,对吗?或者根本无法更新现有类?将属性添加到所有ISettings
实现会起作用,但不是所有类型ISettings
和派生的属性。objectContract.properties
+property.TypeNameHandling
似乎是拼图中缺失的一块。谢谢很高兴它成功了!逆向工程总是这样,干杯!
public class JsonConverterContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
if (contract is JsonObjectContract)
{
var objectContract = contract as JsonObjectContract;
foreach (var property in objectContract.Properties)
{
var propertyType = property.PropertyType;
if (IsTypeOfISettings(propertyType))
{
// setting type name handling on property level
property.TypeNameHandling = TypeNameHandling.None;
}
}
}
// Here I'm hopping to disable type annotations for all `ISettings` instances.
if (IsTypeOfISettings(objectType))
{
if (contract is JsonContainerContract objectContract)
{
objectContract.ItemTypeNameHandling = TypeNameHandling.None;
}
}
return contract;
}
private bool IsTypeOfISettings(Type objectType)
{
return objectType == typeof(ISettings)
|| (objectType.IsClass && objectType.GetInterfaces().Any(i => i == typeof(ISettings)))
|| (objectType == typeof(IReadOnlyList<ISettings>))
|| (objectType == typeof(List<ISettings>))
|| (objectType == typeof(Dictionary<string, ISettings>));
}
}
class AllSettings {
public IDoNotTouchThis MustHaveType => new DoNotTouchThisClass();
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public ISettings MustNotHaveType => new SettingsFoo();
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public IReadOnlyList<ISettings> MustNotHaveTypeEither => new List<ISettings> {
new SettingsFoo(),
new SettingsBar(),
};
}
{
"$type": "jsontest.AllSettings, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.DoNotTouchThisClass, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.HasType, tciypvk5.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
},
"MustNotHaveType": {
"Foo": "NotImportant"
},
"MustNotHaveTypeEither": [
{
"Foo": "NotImportant"
},
{
"Bar": "NotImportant",
"SubSettings": {
"Foo": "NotImportant"
}
}
]
}