序列化对象的自定义$ type值

时间:2018-03-14 16:38:29

标签: c# json.net

我们在串行器设置中使用TypeNameHandling = TypeNameHandling.Objects在Json.Net中使用Web API。这工作正常,但我们只在客户端使用类型信息,从不用于反序列化。我们的序列化对象如下所示:

{
    "$type": "PROJECTNAME.Api.Models.Directory.DtoName, PROJECTNAME.Api",
    "id": 67,
    "offices": [{
        "$type": "PROJECTNAME.Api.Models.Directory.AnotherDtoName, PROJECTNAME.Api",
        "officeName": "FOO"
    }]
},

我想自定义$type属性中的值,使其显示为:

{
    "$type": "Models.Directory.DtoName",
    "id": 67,
    "offices": [{
        "$type": "Models.Directory.AnotherDtoName",
        "officeName": "FOO"
    }]
},

我已经拥有一份继承自CamelCasePropertyNamesContractResolver的合约解析程序。我想我需要做的是关闭TypeNameHandling并自己添加自定义属性。我95%在那里:

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;
}

此时我一直坚持提供物业的价值。

我不确定这是正确的方法,因为Json.Net中的每个ValueProvider类型都使用MemberInfo作为构造函数参数。我不 一个MemberInfo作为参数提供,所以....我被卡住了。

如何添加自定义$type值?由于我没有在C#中进行反序列化,因此我永远不需要将类型信息转换回类型。

1 个答案:

答案 0 :(得分:1)

您应该创建custom ISerializationBinder并覆盖ISerializationBinder.BindToName,而不是添加合成$type属性。在序列化期间调用此方法以指定在启用TypeNameHandling时要发出的类型信息。

例如,以下删除程序集信息以及命名空间的PROJECTNAME.Api.部分:

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对象:

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核心缺少该类型。如果您使用的是10.0.1之前的Json.NET版本,则需要创建该版本的自定义版本。

    另请注意,{。3}}是在.Net 4.0中引入的,因此如果您使用旧版本的Json.NET和旧版本的.Net本身,那么此解决方案将无效。

    < / LI>
  • 由于您没有在c#中进行反序列化,我只是从SerializationBinder.BindToName()引发了异常。但如果有人要实施BindToType(),他们应该注意 BindToType() 的注意事项,并确保对传入的类型进行清理,以防止构建有害类型。