问题描述
我们在序列化程序设置中使用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.
推荐答案
您应该创建自定义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
href ="https://github.com/JamesNK/Newtonsoft.Json/releases/tag/10.0.1" rel ="nofollow noreferrer">版本10.0.1 代替System.Runtime.Serialization.SerializationBinder
,显然是因为某些版本的.Net核心.如果您使用的Json.NET版本低于10.0.1,则需要创建该版本的自定义版本.
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()
,因此,如果您使用的是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 Json中 TypeNameHandling的警告 ,并确保对传入的类型进行清理以防止构造有害类型.
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值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!