json.net - 如何仅在根对象上添加属性 $type [英] json.net - how to add property $type ONLY on root object
问题描述
我想修改我的 json.NET 序列化程序,只将 $type 属性添加到实现给定接口的对象,而不是任何属性或嵌套对象.
使用 TypeNameHandling.Auto(默认)
<代码>{属性A":123,"PropertyB": "foo",属性 C":[1、2、3、4]}
使用 TypeNameHandling.All
<代码>{"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",属性A":123,"PropertyB": "foo",属性C":{"$type": "System.Collections.Generic.List`1[[System.Int32, mscorlib]], mscorlib","$values": [1, 2, 3, 4]}}
我想要什么
<代码>{"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",属性A":123,"PropertyB": "foo",属性 C":[1、2、3、4]}
我正在尝试使用自定义 ContractResolver,但无法正常工作:
类程序{静态无效主要(字符串 [] 参数){var serializerSettings = new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Auto,TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,ContractResolver = new EnableTypeNameHandlingAllOnlyForEvents(),格式 = 格式.缩进};var event1 = new TestEvent() { PropertyA = 123, PropertyB = "foo", PropertyC = new List;{ 1, 2, 3, 4 } };字符串 event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);Console.WriteLine(event1Serialized);Console.ReadLine();}}公共接口 IEvent{}公共类TestEvent:IEvent{公共 int PropertyA { 获取;放;}公共字符串 PropertyB { 获取;放;}公共列表<int>属性C { 得到;放;}}公共类 EnableTypeNameHandlingAllOnlyForEvents : DefaultContractResolver{受保护的覆盖 JsonObjectContract CreateObjectContract(Type objectType){var x = base.CreateObjectContract(objectType);if (typeof(IEvent).IsAssignableFrom(x.UnderlyingType)){//如何告诉 json.NET 将 $type 添加到此 (IEvent) 类型的实例中???}返回 x;}}
如果您需要根对象的 "$type"
属性,并且可以将其显示在嵌套的多态对象和数组中如果需要,请将以下重载与 TypeNameHandling.Auto
一起使用:JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings)
.
来自 文档:
<块引用>公共静态字符串 SerializeObject(对象值,类型类型,JsonSerializerSettings 设置)
类型类型:System.Type被序列化的值的类型.当 TypeNameHandling 为 Auto 时使用此参数,如果值的类型不匹配,则写出类型名称.指定类型是可选的.
即,做:
var serializerSettings = new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Auto,TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,格式 = 格式.缩进};var event1Serialized = JsonConvert.SerializeObject(event1, typeof(IEvent), serializerSettings);
如果您在根对象上需要 "$type"
并且在嵌套的多态对象和数组上不接受它,即使另有要求,您将需要使用TypeNameHandling.All
以及 自定义合约解析器 设置 JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None
:
公共类 SuppressItemTypeNameContractResolver : DefaultContractResolver{受保护的覆盖 JsonContract CreateContract(Type objectType){var contract = base.CreateContract(objectType);var containerContract = 合同为 JsonContainerContract;if (containerContract != null){if (containerContract.ItemTypeNameHandling == null)containerContract.ItemTypeNameHandling = TypeNameHandling.None;}退货合同;}}
然后像这样使用它:
静态 IContractResolver suppressItemTypeNameContractResolver = new SuppressItemTypeNameContractResolver();var serializerSettings = new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All,ContractResolver = suppressItemTypeNameContractResolver,//根据需要进行其他设置.TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,格式 = 格式.缩进};var event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
注意事项:
请注意 Newtonsoft 文档:
<块引用>当您的应用程序从外部源反序列化 JSON 时,应谨慎使用 TypeNameHandling.使用 None 以外的值反序列化时,应使用自定义 SerializationBinder 验证传入类型.
有关为什么这可能是必要的讨论,请参阅 Newtonsoft Json 中的 TypeNameHandling 警告,如何配置 Json.NET 以创建易受攻击的 Web API,以及 Alvaro Muñoz &Oleksandr Mirosh 的黑帽论文 https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
您可能希望静态缓存合约解析器 以获得最佳性能.
I want to modify my json.NET serializer to add the $type property only to the objects which implements a given interface but not to any property or nested objects.
With TypeNameHandling.Auto (default)
{
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": [1, 2, 3, 4]
}
With TypeNameHandling.All
{
"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": {
"$type": "System.Collections.Generic.List`1[[System.Int32, mscorlib]], mscorlib",
"$values": [1, 2, 3, 4 ]
}
}
What I want
{
"$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
"PropertyA": 123,
"PropertyB": "foo",
"PropertyC": [1, 2, 3, 4]
}
I am experimenting with a custom ContractResolver but I don't get it to work:
class Program
{
static void Main(string[] args)
{
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new EnableTypeNameHandlingAllOnlyForEvents(),
Formatting = Formatting.Indented
};
var event1 = new TestEvent() { PropertyA = 123, PropertyB = "foo", PropertyC = new List<int> { 1, 2, 3, 4 } };
string event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
Console.WriteLine(event1Serialized);
Console.ReadLine();
}
}
public interface IEvent
{
}
public class TestEvent : IEvent
{
public int PropertyA { get; set; }
public string PropertyB { get; set; }
public List<int> PropertyC { get; set; }
}
public class EnableTypeNameHandlingAllOnlyForEvents : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var x = base.CreateObjectContract(objectType);
if (typeof(IEvent).IsAssignableFrom(x.UnderlyingType))
{
// What to do to tell json.NET to add $type to instances of this (IEvent) type???
}
return x;
}
}
If you require the "$type"
property on your root object and are OK with it appearing on nested polymorphic objects and arrays if required, use the following overload along with TypeNameHandling.Auto
: JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings)
.
From the docs:
public static string SerializeObject( Object value, Type type, JsonSerializerSettings settings )
type Type: System.Type The type of the value being serialized. This parameter is used when TypeNameHandling is Auto to write out the type name if the type of the value does not match. Specifing the type is optional.
I.e., do:
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, typeof(IEvent), serializerSettings);
If you require "$type"
on the root object and will not accept it on nested polymorphic objects and arrays even if otherwise required, you will need to use TypeNameHandling.All
along with a custom contract resolver that sets JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None
:
public class SuppressItemTypeNameContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
var containerContract = contract as JsonContainerContract;
if (containerContract != null)
{
if (containerContract.ItemTypeNameHandling == null)
containerContract.ItemTypeNameHandling = TypeNameHandling.None;
}
return contract;
}
}
Then use it like:
static IContractResolver suppressItemTypeNameContractResolver = new SuppressItemTypeNameContractResolver();
var serializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
ContractResolver = suppressItemTypeNameContractResolver,
// Other settings as required.
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
var event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);
Notes:
Be aware of this caution from the Newtonsoft docs:
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
You may want to statically cache the contract resolver for best performance.
这篇关于json.net - 如何仅在根对象上添加属性 $type的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!