怎样使JSON.NET忽略对象的关系? [英] How do I make JSON.NET ignore object relationships?

查看:248
本文介绍了怎样使JSON.NET忽略对象的关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个实体框架项目。我想要序列一堆实体类的实例。我必将一起到这些容器类:

I'm working on an Entity Framework project. I want to serialize a bunch of entity class instances. I've bound these together into a container class:

public class Pseudocontext
{
    public List<Widget> widgets;
    public List<Thing> things;



诸如此类......这是这个类,我试图序列化的一个实例。我想JSON.NET序列化的实际上是底层数据库列的每个实体类实例的成员。我不希望它甚至试图序列化对象的引用。

Etcetera... it is an instance of this class that I'm attempting to serialize. I want JSON.NET to serialize the members of each entity class instance that are actually columns in the underlying database. I do not want it to even attempt to serialize object references.

在特别的,我的实体类有虚拟成员,让我写一个导航我所有的接口的C#代码不用担心实际键值的实体关系,联接,等等,我想JSON.NET忽略我的实体类的相关部分。

In particular, my entity classes have virtual members that allow me to write C# code that navigates all my inter-entity relationships without worrying about actual key values, joins, etc., and I want JSON.NET to ignore the associated parts of my entity classes.

在从表面上看,似乎是不正是我在谈论JSON.NET配置选项:

On the surface, there seems to be a JSON.NET configuration option that does exactly what I'm talking about:

JsonSerializer serializer = new JsonSerializer();
serializer.PreserveReferencesHandling = PreserveReferencesHandling.None;



不幸的是,JSON.NET似乎被忽略了上面的第二个声明。

Unfortunately, JSON.NET seems to be ignoring the second statement above.

我居然发现一个网页( http://json.codeplex.com/工作项/ 24608 ),其中有人提出了同样的问题,以詹姆斯·牛顿 - 国王本人,他的回答的关注(整体)为写一个自定义的合同解析器。

I actually found a web page (http://json.codeplex.com/workitem/24608) where someone else brought the same issue to the attention of James Newton-King himself, and his response (in its entirety) was "Write a custom contract resolver."

由于不够的,因为我觉得这反应是,我一直在试图遵循的指导。我非常希望能够写有合同解析即忽略一切,除了基本类型,字符串,日期时间对象,我自己Pseudocontext类直接包含列表一起。如果有人有东西,至少类似于一个例子,这可能是我所需要的。这是我想出了我自己的:

As inadequate as I find that response to be, I have been attempting to follow its guidance. I would very much like to be able to write a "contract resolver" that ignored everything except primitive types, strings, DateTime objects, and my own Pseudocontext class along with the Lists it contains directly. If someone has an example of something that at least resembles that, it might be all I need. This is what I came up with on my own:

public class WhatDecadeIsItAgain : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type objectType)
    {
        JsonContract contract = base.CreateContract(objectType);
        if (objectType.IsPrimitive || objectType == typeof(DateTime) || objectType == typeof(string)
            || objectType == typeof(Pseudocontext) || objectType.Name.Contains("List"))
        {
            contract.Converter = base.CreateContract(objectType).Converter;
        }
        else
        {
            contract.Converter = myDefaultConverter;
        }
        return contract;
    }
    private static GeeThisSureTakesALotOfClassesConverter myDefaultConverter = new GeeThisSureTakesALotOfClassesConverter();
}

public class GeeThisSureTakesALotOfClassesConverter : Newtonsoft.Json.Converters.CustomCreationConverter<object>
{
    public override object Create(Type objectType)
    {
        return null;
    }
}

当我尝试使用上面的(通过设置串行.ContractResolver到WhatDecadeIsItAgain之前序列化)的一个实例,我得到的指示是JSON.NET遇到参考循环,从来没有终止(尽管我努力使JSON.NET的序列化过程中出现内存不足错误的只是忽略对象引用)。

When I attempt to use the above (by setting serializer.ContractResolver to an instance of WhatDecadeIsItAgain prior to serialization), I get OutOfMemory errors during serialization that indicate that JSON.NET is encountering reference loops that never terminate (in spite of my efforts to make JSON.NET just ignore object references).

我觉得像我的定制合同解析器可能是错误的。如上图所示,它的建成周围,我应该返回我想要序列类型默认的合同的前提下,和一个合同,简单地返回空为所有其他类型。

I feel like my "custom contract resolver" may be wrong. As shown above, it's built around the premise that I should return the default "contract" for the types I do want to serialize, and a "contract" that simply returns "null" for all other types.

我不知道这些假设正确怎样是,虽然,这并不容易分辨。该JSON.NET设计是非常基础上实现继承,方法重载等;我没有多大的 OOP 的家伙,我觉得那种设计的是相当模糊。在那里,我可以实现定制合同解决程序界面,的Visual Studio 2012 会能够非常快速存根出所需的方法,和我想象我有一点麻烦填补与现实逻辑的存根。

I have no idea how correct these assumptions are, though, and it's not easy to tell. The JSON.NET design is very much based on implementation inheritance, method overriding, etc.; I'm not much of an OOP guy, and I find that sort of design to be pretty obscure. Were there a "custom contract resolver" interface that I could implement, Visual Studio 2012 would be able to stub out the required methods very quickly, and I imagine I'd have little trouble filling the stubs in with real logic.

我有没有问题写作,例如,返回true如果我想序列化提供类型的对象和假,否则的方法。也许我失去了一些东西,但我没有发现这样的方法来覆盖,也没有我能找到的假想界面(ICustomContractResolver?)那会告诉我什么,我实际上应该在最后的代码片段做上方插入。

I'd have no problem writing, for example, a method that returns "true" if I want to serialize an object of a supplied type and "false" otherwise. Perhaps I'm missing something, but I've found no such method to override, nor have I been able to find the hypothetical interface (ICustomContractResolver ?) that would tell me what I'm actually supposed to be doing in the last code snippet inserted above.

另外,我认识到,有JSON.NET属性被设计来处理这样的情况下([JsonIgnore])。我真的不能使用该方法,因为我使用的模式第一。除非我决定要撕了我的整个项目的建筑,我的实体类会自动生成,它们将不包含JsonIgnore属性,我也不觉得舒服编辑自动化类以包含这些属性。

Also, I realize that there are JSON.NET attributes ([JsonIgnore]?) that are designed to deal with situations like this. I can't really use that approach, since I'm using "model first". Unless I decide to tear up my entire project architecture, my entity classes will be automatically generated, and they will not contain JsonIgnore attributes, nor do I feel comfortable editing the automated classes to contain these attributes.

顺便说一下,一会儿我的没有的有事情成立了序列化对象引用,而我只是忽略所有多余的$ REF和$ ID数据JSON。 NET返航其序列化输出。我已经放弃了,至少在那一刻的办法,因为(陡然)序列开始采取过多的时间量(〜45分钟搞定〜5&NBSP; MB JSON的)。

Incidentally, for a while I did have things set up to serialize object references, and I was just ignoring all the superfluous "$ref" and "$id" data that JSON.NET was returning in its serialization output. I've abandoned that approach for the moment at least, because (rather suddenly) serialization started taking an inordinate amount of time (~45 minutes to get ~5 MB of JSON).

我一直没能配合业绩的突然变回具体,我做了什么。如果有的话,数据在我的数据库体积下现在比当系列化合理的时间实际完成。但我更乐意用一回的原状的(其中我只是有忽略$ REF,$ ID等),如果可以实现

I haven't been able to tie that sudden change in performance back to anything specific that I did. If anything, the volume of data in my database is lower now than it was when serialization was actually completing in reasonable time. But I'd be more than happy with a return to the status quo ante (in which I was just having to ignore "$ref", "$id", etc.) if that could be achieved.

在这一点上,我也开到使用一些其他的JSON库,或不同的策略完全的前景。我觉得我可以只使用StringBuilder的,的System.Reflection等,并与我自己的,自制的解决方案来的......但不JSON.NET应该能够很轻松地处理这样的事情?

At this point, I'm also open to the prospect of using some other JSON library, or a different strategy altogether. I feel like I could just use StringBuilder, System.Reflection, etc. and come of with my own, homemade solution... but isn't JSON.NET supposed to be able to handle this sort of thing pretty easily??

推荐答案

首先,参照loops--的 PreserveReferencesHandling 设置解决您的问题控制是否Json.Net发出的$ id $ REF 来跟踪对象间引用。如果你有这样的设置为和你的对象图包含循环,那么你也将需要设置 ReferenceLoopHandling 忽略以避免错误。

First, to address your issues with reference loops-- The PreserveReferencesHandling setting controls whether Json.Net emits $id and $ref to track inter-object references. If you have this set to None and your object graph contains loops, then you will also need to set ReferenceLoopHandling to Ignore to prevent errors.

现在,让Json.Net完全忽略所有的对象引用,只有序列基本属性(除非你的 Pseudocontext 类当然),你就需要自定义合同解析器,如你所说。不过不要担心,这是并不难,因为你的想法。解析器有注入每个属性一个 ShouldSerialize 的方法来控制该财产是否应该被包含在输出的能力。所以,你需要做的是从默认的派生解析器,然后重写 CreateProperty 的方法,例如,它集 ShouldSerialize 得体。 (您不需要自定义的 JsonConverter 在这里,虽然有可能与相当多的代码虽然解决了这个问题,该办法。)

Now, to get Json.Net to ignore all object references altogether and only serialize primitive properties (except in your Pseudocontext class of course), you do need a custom Contract Resolver, as you suggested. But don't worry, it is not as hard as you think. The resolver has the capability to inject a ShouldSerialize method for each property to control whether or not that property should be included in the output. So, all you need to do is derive your resolver from the default one, then override the CreateProperty method such that it sets ShouldSerialize appropriately. (You do not need a custom JsonConverter here, although it is possible to solve this problem with that approach albeit with quite a bit more code.)

下面是解析的代码:

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        if (prop.DeclaringType != typeof(PseudoContext) && 
            prop.PropertyType.IsClass && 
            prop.PropertyType != typeof(string))
        {
            prop.ShouldSerialize = obj => false;
        }

        return prop;
    }
}

下面是显示在行动解析一个完整的演示。

Here is a full demo showing the resolver in action.

class Program
{
    static void Main(string[] args)
    {
        // Set up some dummy data complete with reference loops
        Thing t1 = new Thing { Id = 1, Name = "Flim" };
        Thing t2 = new Thing { Id = 2, Name = "Flam" };

        Widget w1 = new Widget
        {
            Id = 5,
            Name = "Hammer",
            IsActive = true,
            Price = 13.99M,
            Created = new DateTime(2013, 12, 29, 8, 16, 3),
            Color = Color.Red,
        };
        w1.RelatedThings = new List<Thing> { t2 };
        t2.RelatedWidgets = new List<Widget> { w1 };

        Widget w2 = new Widget
        {
            Id = 6,
            Name = "Drill",
            IsActive = true,
            Price = 45.89M,
            Created = new DateTime(2014, 1, 22, 2, 29, 35),
            Color = Color.Blue,
        };
        w2.RelatedThings = new List<Thing> { t1 };
        t1.RelatedWidgets = new List<Widget> { w2 };

        // Here is the container class we wish to serialize
        PseudoContext pc = new PseudoContext
        {
            Things = new List<Thing> { t1, t2 },
            Widgets = new List<Widget> { w1, w2 }
        };

        // Serializer settings
        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();
        settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
        settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        settings.Formatting = Formatting.Indented;

        // Do the serialization and output to the console
        string json = JsonConvert.SerializeObject(pc, settings);
        Console.WriteLine(json);
    }

    class PseudoContext
    {
        public List<Thing> Things { get; set; }
        public List<Widget> Widgets { get; set; }
    }

    class Thing
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Widget> RelatedWidgets { get; set; }
    }

    class Widget
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
        public decimal Price { get; set; }
        public DateTime Created { get; set; }
        public Color Color { get; set; }
        public List<Thing> RelatedThings { get; set; }
    }

    enum Color { Red, White, Blue }
}

输出:

{
  "Things": [
    {
      "Id": 1,
      "Name": "Flim"
    },
    {
      "Id": 2,
      "Name": "Flam"
    }
  ],
  "Widgets": [
    {
      "Id": 5,
      "Name": "Hammer",
      "IsActive": true,
      "Price": 13.99,
      "Created": "2013-12-29T08:16:03",
      "Color": 0
    },
    {
      "Id": 6,
      "Name": "Drill",
      "IsActive": true,
      "Price": 45.89,
      "Created": "2014-01-22T02:29:35",
      "Color": 2
    }
  ]
}

希望这是在你要找的球场。

Hope this is in the ballpark of what you were looking for.

这篇关于怎样使JSON.NET忽略对象的关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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