【问题标题】:Omit type for dynamic property without modifying class?在不修改类的情况下省略动态属性的类型?
【发布时间】:2016-09-08 08:33:47
【问题描述】:

我有一堆我序列化的域对象,发送到其他应用程序,然后使用Json.Net 反序列化。这些对象可能具有以下属性

  • 定义为具有多个派生类的抽象基类
  • 动态属性

我使用了TypeNameHandling.Auto,它将$type 属性添加到与声明类型不同的类型。然而,这个设置对我的动态属性有一个不想要的副作用,即它们的类型也被声明了。

在下面的示例中,model 是我的 C# 代码中定义为 public dynamic Model { get; set; } 的动态属性。

"model":{"$type":"<>f__AnonymousType0`3[[System.String, mscorlib],[System.String, mscorlib],[System.String, mscorlib]], ExampleAssembly","link":"http://www.google.com","name":"John"}

当试图在另一个程序集中反序列化这个字符串时,Json.Net(当然)找不到ExampleAssembly。使用TypeNameHandling.None 属性给出以下属性序列化

"model": {"link":"http://www.google.com","name":"John"}

可以成功反序列化为dynamic。但是,这会破坏派生类型的反序列化。

关于如何在不实现自定义 IContractResolver 和可能的其他自定义代码的情况下使其工作的任何想法?

我不拥有域对象,所以我不能用属性装饰它们或它们的属性或允许它们实现接口等。我正在寻找的是序列化程序中省略类型的某种设置dynamics.

恕我直言,这应该可以通过设置以某种方式进行配置,我只是还没找到。

【问题讨论】:

  • [JsonIgnore] 或条件序列化 (newtonsoft.com/json/help/html/conditionalproperties.htm) 可能有用
  • @Nair:谢谢,但不幸的是没有。我不拥有域对象,因此无法使用属性或方法进行装饰。此外,这不能很好地扩展,因为我必须对所有对象都这样做。
  • [JsonProperty(TypeNameHandling = TypeNameHandling.None)] 添加到相关属性将是最直接的方法 - 但在您的评论中您说您不能这样做。你可能想edit这个问题来澄清这个要求。
  • @dbc 此外,我不确定我是否同意这是最直接的,因为我必须(如果我能够)将此属性添加到数百个属性中(并继续将它们添加为引入了新类)。在我看来,序列化信息属于序列化器,而不是它们序列化的对象。
  • 如果您无法更改您的类型,那么自定义合同解析器似乎是解决方案 - 但您的问题表明您不希望这样做。

标签: c# .net json serialization json.net


【解决方案1】:

Json.Net 不提供特定设置来仅关闭动态类型的类型名称处理。如果您不能(或不想)用[JsonProperty(TypeNameHandling = TypeNameHandling.None)] 标记相关的动态属性,那么您唯一的其他选择(不修改 Json.Net 源代码本身)是实现自定义合约解析器以应用以编程方式进行行为。不过不用担心,如果您从 Json.Net 提供的解析器之一(例如 DefaultContractResolverCamelCasePropertyNamesContractResolver)派生解析器,这并不难。

这是您需要的所有代码:

using System;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public class OmitTypeNamesOnDynamicsResolver : DefaultContractResolver
{
    public static readonly OmitTypeNamesOnDynamicsResolver Instance = new OmitTypeNamesOnDynamicsResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        if (member.GetCustomAttribute<System.Runtime.CompilerServices.DynamicAttribute>() != null)
        {
            prop.TypeNameHandling = TypeNameHandling.None;
        }
        return prop;
    }
}

然后,只需将解析器添加到 JsonSerializerSettings 即可。

JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto,
    ContractResolver = OmitTypeNamesOnDynamicsResolver.Instance
};

string json = JsonConvert.SerializeObject(foo, settings);

这里有一个往返演示来证明这个概念:

public class Program
{
    public static void Main(string[] args)
    {
        Foo foo = new Foo
        {
            Model = new { link = "http://www.google.com", name = "John" },
            Widget1 = new Doodad { Name = "Sprocket", Size = 10 },
            Widget2 = new Thingy { Name = "Coil", Strength = 5 }
        };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            ContractResolver = OmitTypeNamesOnDynamicsResolver.Instance,
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(foo, settings);
        Console.WriteLine(json);
        Console.WriteLine();

        Foo foo2 = JsonConvert.DeserializeObject<Foo>(json, settings);
        Console.WriteLine(foo2.Model.link);
        Console.WriteLine(foo2.Model.name);
        Console.WriteLine(foo2.Widget1.Name + " (" + foo2.Widget1.GetType().Name + ")");
        Console.WriteLine(foo2.Widget2.Name + " (" + foo2.Widget2.GetType().Name + ")");
    }
}

public class Foo
{
    public dynamic Model { get; set; }
    public AbstractWidget Widget1 { get; set; }
    public AbstractWidget Widget2 { get; set; }
}

public class AbstractWidget
{
    public string Name { get; set; }
}

public class Thingy : AbstractWidget
{
    public int Strength { get; set; }
}

public class Doodad : AbstractWidget
{
    public int Size { get; set; }
}

输出:

{
  "Model": {
    "link": "http://www.google.com",
    "name": "John"
  },
  "Widget1": {
    "$type": "Doodad, JsonTest",
    "Size": 10,
    "Name": "Sprocket"
  },
  "Widget2": {
    "$type": "Thingy, JsonTest",
    "Strength": 5,
    "Name": "Coil"
  }
}

http://www.google.com
John
Sprocket (Doodad)
Coil (Thingy)

【讨论】:

  • 漂亮!正如我所说,我宁愿拥有一些没有自定义类的开箱即用功能。但是,您对 Contact Resolver 的实现非常小,对此表示赞赏。感谢您写得很好!
  • 没问题;很高兴我能帮上忙。
猜你喜欢
  • 2011-05-25
  • 1970-01-01
  • 2012-10-22
  • 2011-10-31
  • 2010-12-24
  • 2021-07-06
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多