在不修改类的情况下省略动态属性的类型? [英] Omit type for dynamic property without modifying class?

查看:92
本文介绍了在不修改类的情况下省略动态属性的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆域对象,我将它们序列化,发送给其他应用程序,然后使用Json.Net反序列化.这些对象的属性可能是

I have a bunch of domain objects that I serialize, send to other applications and then deserialize using Json.Net. These objects may have properties that are

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

我使用了TypeNameHandling.Auto,它将$type属性添加到与声明的类型不同的类型.但是,此设置会对我的动态属性产生不良影响,即它们的类型也会被声明.

I've used TypeNameHandling.Auto, which adds the $type property to types that differ from the declared type. However, this setting has an unwanted side effect on my dynamic properties, namely that the types of them gets declared, too.

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

In the example below model is a dynamic property defined as public dynamic Model { get; set; } in my C# code.

"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属性提供以下属性序列化

When trying to deserialize this string in another assembly, Json.Net can't (of course) find the ExampleAssembly. Using the TypeNameHandling.None property gives the following property serialization

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

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

Which can be successfully deserialized to a dynamic. However, this breaks deserialization of derived types.

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

Any ideas on how to get this to work without implementing custom IContractResolver and possibly other custom code?

我不拥有域对象,因此无法用属性修饰它们或它们的属性,也不能允许它们实现接口等.我要寻找的是序列化程序中的某种设置,该设置忽略了类型dynamics.

I don't own the domain objects, so I can't decorate them or their properties with attributes or allow them to implement interfaces etc. What I'm looking for is some sort of setting in the serializer that omits types for dynamics.

恕我直言,这应该可以通过设置以某种方式配置,我只是没有找到它.

IMHO this should be configurable through settings somehow, I just havn't found it.

推荐答案

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

Json.Net does not provide a specific setting to turn off type name handling for dynamic types only. If you can't (or don't want to) mark the relevant dynamic properties with [JsonProperty(TypeNameHandling = TypeNameHandling.None)], then your only other option (short of modifying the Json.Net source code itself) is to implement a custom contract resolver to apply the behavior programmatically. But don't worry, this is not difficult to do if you derive your resolver from one of the Json.Net-provided resolvers such as DefaultContractResolver or CamelCasePropertyNamesContractResolver.

这是您需要的所有代码:

Here is all the code you would need:

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中,就应该准备就绪.

Then, just add the resolver to the JsonSerializerSettings and you should be all set.

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

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

以下是一个演示该概念的往返演示:

Here is a round-trip demo to prove the concept:

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)

这篇关于在不修改类的情况下省略动态属性的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆