【问题标题】:Custom $type value for serialized objects序列化对象的自定义 $type 值
【发布时间】:2018-03-14 16:38:28
【问题描述】:

我们在序列化程序设置中使用TypeNameHandling = TypeNameHandling.Objects 将Web API 与Json.Net 一起使用。这很好用,但我们只在客户端使用类型信息,从不用于反序列化。我们的序列化对象如下所示:

{
    "$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# 中进行反序列化,因此我永远不需要将类型信息转换回类型。

【问题讨论】:

标签: c# json.net


【解决方案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);

注意事项:

【讨论】:

  • 一个很好的答案,谢谢先生/女士您的时间和精力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多