问题描述
我们在序列化程序设置中使用 TypeNameHandling = TypeNameHandling.Objects
将 Web API 与 Json.Net 一起使用.这很好用,但我们只在客户端使用类型信息,从不用于反序列化.我们的序列化对象如下所示:
We're using Web API with Json.Net using TypeNameHandling = TypeNameHandling.Objects
in our serializer settings. This works fine, but we use the type information only client-side, never for deserialization. Our serialized objects look like this:
{
"$type": "PROJECTNAME.Api.Models.Directory.DtoName, PROJECTNAME.Api",
"id": 67,
"offices": [{
"$type": "PROJECTNAME.Api.Models.Directory.AnotherDtoName, PROJECTNAME.Api",
"officeName": "FOO"
}]
},
我想自定义 $type
属性中的值,使其显示为:
I would like to customize the value in the $type
property so it reads as:
{
"$type": "Models.Directory.DtoName",
"id": 67,
"offices": [{
"$type": "Models.Directory.AnotherDtoName",
"officeName": "FOO"
}]
},
我已经有一个继承自 CamelCasePropertyNamesContractResolver
的合同解析器.我想我需要做的是关闭 TypeNameHandling
并自己添加一个自定义属性.我有 95% 在那里:
I already have a contract resolver that inherits from CamelCasePropertyNamesContractResolver
. I figure what I need to do is turn off TypeNameHandling
and add a custom property myself. I'm 95% there:
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var assemblyName = type.Assembly.GetName().Name;
var typeName = type.FullName.Substring(assemblyName.Length + 1);
var typeProperty = new JsonProperty()
{
PropertyName = "$type",
PropertyType = typeof(string),
Readable = true,
Writable = true,
ValueProvider = null // ????? typeName
};
var retval = base.CreateProperties(type, memberSerialization);
retval.Add(typeProperty);
return retval;
}
在这一点上,我坚持提供财产的价值.
At this point I'm stuck with supplying the property's value.
我不确定这是不是正确的方法,因为 Json.Net 中的每个 ValueProvider
类型都将 MemberInfo
作为构造函数参数.我没有有 MemberInfo
作为参数提供,所以....我被卡住了.
I'm unsure that this is the correct approach because each of the ValueProvider
types from Json.Net take a MemberInfo
as a constructor parameter. I don't have a MemberInfo
to supply as a parameter, so.... I'm stuck.
如何添加自定义 $type
值?由于我没有在 C# 中进行反序列化,因此我永远不需要将类型信息转换回类型.
How do I add a custom $type
value? Since I'm not doing deserialization in C# I will never need to convert the type information back into a type.
推荐答案
与其添加合成的 $type
属性,不如创建一个 自定义 ISerializationBinder
并覆盖 ISerializationBinder.BindToName
.在序列化期间调用此方法以指定在 TypeNameHandling 时发出的类型信息
已启用.
Rather than adding a synthetic $type
property, you should create a custom ISerializationBinder
and override ISerializationBinder.BindToName
. This method is called during serialization to specify the type information to emit when TypeNameHandling
is enabled.
例如,以下内容会去除程序集信息以及命名空间的 PROJECTNAME.Api.
部分:
For instance, the following strips the assembly information as well as the PROJECTNAME.Api.
portion of the namespace:
public class MySerializationBinder : ISerializationBinder
{
const string namespaceToRemove = "PROJECTNAME.Api.";
readonly ISerializationBinder binder;
public MySerializationBinder() : this(new Newtonsoft.Json.Serialization.DefaultSerializationBinder()) { }
public MySerializationBinder(ISerializationBinder binder)
{
if (binder == null)
throw new ArgumentNullException();
this.binder = binder;
}
#region ISerializationBinder Members
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
binder.BindToName(serializedType, out assemblyName, out typeName);
if (typeName != null && typeName.StartsWith(namespaceToRemove))
typeName = typeName.Substring(namespaceToRemove.Length);
assemblyName = null;
}
public Type BindToType(string assemblyName, string typeName)
{
throw new NotImplementedException();
}
#endregion
}
然后你可以用它序列化你的 DtoName
对象,如下所示:
Then you can serialize your DtoName
object with it as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
SerializationBinder = new MySerializationBinder(),
TypeNameHandling = TypeNameHandling.Objects,
};
var json = JsonConvert.SerializeObject(dto, Formatting.Indented, settings);
注意事项:
Newtonsoft 推出
ISerializationBinder
在 release 10.0.1 中替代System.Runtime.Serialization.SerializationBinder
,显然是因为某些版本的 .Net core 中缺少该类型.如果您使用的是 10.0.1 之前的 Json.NET 版本,则需要创建其自定义版本.
Newtonsoft introduced
ISerializationBinder
in release 10.0.1 as a replacement toSystem.Runtime.Serialization.SerializationBinder
, apparently because that type is missing in some versions of .Net core. If you are using a version of Json.NET that precedes 10.0.1 you will need to create a custom version of that instead.
还要注意 SerializationBinder.BindToName()
是在 .Net 4.0 中引入的,因此如果您使用的是旧版本的 Json.NET 和旧版本的 .Net 本身,那么此解决方案将不工作.
Note also that SerializationBinder.BindToName()
was introduced in .Net 4.0, so if you are using an old version of Json.NET and an old version of .Net itself then this solution will not work.
由于您没有在 c# 中进行反序列化,我只是从 BindToType()
.但是如果有人要实现 BindToType()
,他们应该注意 Newtonsoft 中 TypeNameHandling 的警告Json 并确保清理传入类型以防止构建有害类型.
As you are not doing deserialization in c# I simply threw an exception from BindToType()
. But if someone were to implement BindToType()
, they should take heed of the caution from TypeNameHandling caution in Newtonsoft Json and be sure to sanitize the incoming types to prevent construction of harmful types.
这篇关于序列化对象的自定义 $type 值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!