不,你没有遗漏任何明显的东西。由于您已经看到的原因,尝试使用处理各种类的JsonConverter 来做到这一点将会有问题。 JsonConverters 最适合处理特定类型;他们不太擅长概括。
幸运的是,有一种方法可以使用自定义 IContractResolver 来做您想做的事情。
合约解析器允许我们在属性级别对各种类应用某些序列化行为。这个想法是让解析器在每个类上伪造一个额外的“类型名称”属性(或任何您想要调用的属性)并安装相应的IValueProvider 以在序列化每个类时为该属性提供值目的。 (这样,序列化程序永远不会知道该属性并不真正存在。)
创建解析器的最简单方法是从 Json.Net 附带的 DefaultContractResolver 派生它。从那里我们只需要重写 CreateProperties() 方法并将我们的假属性注入从基类返回的列表中。
这里是解析器和值提供者的代码:
class CustomResolver : DefaultContractResolver
{
private string customTypePropertyName;
private IValueProvider valueProvider = new SimpleTypeNameProvider();
public CustomResolver(string customTypePropertyName)
{
this.customTypePropertyName = customTypePropertyName;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
if (type.IsClass && type != typeof(string))
{
// Add a phantom string property to every class which will resolve
// to the simple type name of the class (via the value provider)
// during serialization.
props.Insert(0, new JsonProperty
{
DeclaringType = type,
PropertyType = typeof(string),
PropertyName = customTypePropertyName,
ValueProvider = valueProvider,
Readable = true,
Writable = false
});
}
return props;
}
class SimpleTypeNameProvider : IValueProvider
{
public object GetValue(object target)
{
return target.GetType().Name;
}
public void SetValue(object target, object value)
{
return;
}
}
}
要使用解析器,请创建一个实例并通过 JsonSerializerSettings 对象将其传递给序列化器。这是一个简短的演示:
class Program
{
static void Main(string[] args)
{
Person p = new Person
{
Id = 2,
Name = "Peter",
Employer = new Company
{
Id = 5,
Name = "Initech"
}
};
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver("MyTypeName"),
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(p, settings);
Console.WriteLine(json);
}
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Company Employer { get; set; }
}
class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
输出:
{
"MyTypeName": "Person",
"Id": 2,
"Name": "Peter",
"Employer": {
"MyTypeName": "Company",
"Id": 5,
"Name": "Initech"
}
}