在远离$type注释的过程中(为了使数据语言独立),我在理解各种TypeNameHandling注释和类型契约的优先级方面遇到了一些问题。
在转换过程中,新类型和旧类型都将包含在相同的文件中。因此,该文件必须包含除新类型(ISettings实现)之外的所有类型的注释。
我试图用以下最小示例重现我的问题:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过这个例子,我们得到以下信息:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
代替
{
// Not necessary, but doesn't hurt:
"$type": "jsontest.AllSettings, pzyc3cmg.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.DoNotTouchThisClass, pzyc3cmg.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"MustHaveType": {
"$type": "jsontest.HasType, pzyc3cmg.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
},
"MustNotHaveType": {
"$type": "jsontest.SettingsFoo, pzyc3cmg.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"Foo": "NotImportant"
},
"MustNotHaveTypeEither": {
"$type": "System.Collections.Generic.List`1[[jsontest.ISettings, pzyc3cmg.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e",
"$values": [
{
"Foo": "NotImportant"
},
{
"Bar": "NotImportant",
"SubSettings": {
"Foo": "NotImportant"
}
}
]
}
}
Run Code Online (Sandbox Code Playgroud)
我试过在不同的地方应用TypeNameHandling/ItemTypeNameHandling注释但没有成功。我正在寻找的格式是否可以使用 Json.NET?
更新:
我找到了一种在方法中动态设置属性的方法TypeNameHandling,JsonObjectContract这CreateContract似乎已经解决了问题。在这里查看 .Net fiddler :
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>));
}
}
Run Code Online (Sandbox Code Playgroud)
原答案:
我相信是的,看看这个更新的.NET fiddle。我在这个小提琴中所做的唯一更改是替换ItemTypeNameHandling为TypeNameHandling类AllSettings属性,并且它有效!我通过查看 Newtonsoft.Json 的内部工作(特别是序列化器编写器)获得了灵感。
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(),
};
}
Run Code Online (Sandbox Code Playgroud)
结果看起来像:
{
"$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"
}
}
]
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
217 次 |
| 最近记录: |