当前位置: 首页 > 面试题库 >

如何使JSON.NET忽略对象关系?

宋飞舟
2023-03-14
问题内容

我正在一个实体框架项目。我想序列化一堆实体类实例。我将它们绑定到一个容器类中:

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

Etcetera
…这是我尝试序列化的此类的一个实例。我希望JSON.NET序列化每个实际上是基础数据库中的列的每个实体类实例的成员。我甚至不想尝试序列化对象引用。

特别是,我的实体类具有虚拟成员,这些成员使我能够编写可导航所有实体关系的C#代码,而不必担心实际的键值,联接等问题,并且我希望JSON.NET忽略我的实体的关联部分类。

从表面上看,似乎有一个JSON.NET配置选项可以完全满足我的要求:

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

不幸的是,JSON.NET似乎忽略了上面的第二条语句。

我实际上发现了一个网页(http://json.codeplex.com/workitem/24608),其中有人引起了James
Newton-King本人的注意,他的回答(全部)是“写一个自定义合同解析器。”

尽管我认为该答复不够充分,但我一直在尝试遵循其指导意见。我非常希望能够编写一个“合同解析器”,该协议将忽略除原始类型,字符串,DateTime对象和我自己的Pseudocontext类以及其直接包含的Lists之外的所有内容。如果某人的例子至少与之相似,那可能就是我所需要的。这是我自己想到的:

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

当我尝试使用以上代码时(通过在序列化之前将serializer.ContractResolver设置为WhatDecadeIsItAgain的实例),我在序列化过程中遇到OutOfMemory错误,表明JSON.NET遇到了永不终止的引用循环(尽管我做了很多努力)
JSON.NET 只是忽略对象引用 )。

我觉得我的“自定义合同解析器”可能是错误的。如上所示,它建立在这样一个前提下:我应该为我要序列化的类型返回默认的“
contract”,而为所有其他类型仅返回“ null”的“ contract”。

我不知道这些假设有多正确,而且很难说出来。JSON.NET的设计很大程度上基于实现继承,方法重写等。我不是一个面向对象的家伙,我发现这种设计非常晦涩。如果有一个我可以实现的“自定义合同解析器”接口,Visual Studio
2012将能够非常迅速地存根所需的方法,并且我想我用真正的逻辑填充存根几乎没有什么麻烦。

我编写时没有问题,例如,如果我要序列化提供类型的对象,则返回“ true”的方法,否则返回“
false”的方法。也许我缺少了一些东西,但是我没有找到可以覆盖的方法,也没有找到一个假设的接口(ICustomContractResolver?),该接口可以告诉我在上一个代码片段中我实际上应该做什么插入上方。

另外,我意识到有JSON.NET属性([JsonIgnore]?)专用于处理此类情况。我真的不能使用这种方法,因为我使用的是“模型优先”。除非我决定拆除整个项目体系结构,否则将自动生成我的实体类,并且它们将不包含JsonIgnore属性,也不会为编辑包含这些属性的自动化类感到自在。

顺便说一句,我 确实做 了一些设置来对对象引用进行序列化,而我只是忽略了JSON.NET在其序列化输出中返回的所有多余的“ $ ref”和“ $
id”数据。我至少暂时放弃了这种方法,因为(而不是突然)序列化开始花费了过多的时间(〜45分钟才能获得〜5 MB的JSON)。

我无法将性能的突然变化与我所做的任何具体事情联系起来。如果有的话,我的数据库中的数据量现在比序列化实际上在合理的时间内完成时的数据量要少。但是,如果可以做到这一点,我会对返回
原状 (在其中我只需要忽略“ $ ref”,“ $ id”等)感到非常满意。

在这一点上,我也对使用其他JSON库或完全不同的策略持开放态度。我觉得我可以只使用StringBuilder,System.Reflection等,并提供自己的自制解决方案…但是JSON.NET不能轻易处理这种事情吗?


问题答案:

首先,要解决引用循环的问题-该PreserveReferencesHandling设置控制Json.Net是否发出$id$ref跟踪对象间引用。如果你有这一套来None和你的对象图包含循环,那么你也将需要设置ReferenceLoopHandling,以Ignore防止发生错误。

现在,要使Json.Net完全忽略所有对象引用并仅序列化基本属性(Pseudocontext当然,在您的类中除外),您确实需要按照您的建议自定义Contract
Resolver。但是不用担心,它并不像您想的那么难。解析器可以ShouldSerialize为每个属性注入一种方法,以控制该属性是否应包含在输出中。因此,您所需要做的就是从默认解析器派生您的解析器,然后重写该CreateProperty方法以使其ShouldSerialize适当设置。(JsonConverter尽管可以使用该方法解决此问题,但您不需要自定义。但是,这将需要更多代码。)

这是解析器的代码:

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

这是一个完整的演示,显示了运行中的解析器。

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

希望这是您要找的东西。



 类似资料:
  • 问题内容: Json.NET文档说您过去不对类中的某些属性进行序列化: 在使用序列化第3方对象时,如何使Json.NET忽略特定属性? 问题答案: 制作自定义合同解析器: 我如何测试它:

  • 问题内容: 我有一些必须序列化为JSON的数据。我正在使用JSON.NET。我的代码结构与此类似: 问题是,我的JSON输出需要具有ON OR 或ON- 取决于所使用的字段(即不为null)。默认情况下,我的JSON如下所示: 我知道我可以使用,但这给了我如下所示的JSON: 我需要的是: 有没有简单的方法可以做到这一点? 问题答案: 是的,您需要使用。 但是由于结构是值类型,因此您需要将Fiel

  • 问题内容: 我使用的最后一个版本,此刻火力地堡的依赖,这是1.0.2的,我有问题,到让我的POJO的正确分析。 问题是,架构随时都可以更改,但我不希望我的应用程序崩溃: D / AndroidRuntime(14097):关闭VM W / dalvikvm(14097):threadid = 1:线程退出且未捕获到异常(group = 0x40a451f8)E / AndroidRuntime(1

  • 我在一个名为的类中有这段代码: 序列化项目对象时,将调用方法,并将字段添加到序列化对象。 如果在方法之前添加,将得到相同的结果。所以我想知道如何实现一个方法,这个方法在序列化对象时被Jackson忽略了。 或者我可以做: 但是解决方案看起来有点难看,而且我相当肯定Jackson提供的一定有某种更简单的机制。 我是不是漏掉了什么?TIA

  • 问题内容: 这个问题本质上是相反的这一个 我有这样的方法: 当我加载它时,Hibernate抱怨我没有称为的属性。但是我不想要一个叫做-我不需要存储数据的属性-这仅仅是逻辑。 hibernate状态: org.hibernate.PropertyNotFoundException:在com.mycomp.myclass类中找不到空置属性的设置器… 我可以在方法中添加注释以使Hibernate忽略它

  • 问题内容: 我正在尝试为api 编写Web服务。因此,我编写了一个MyLogRecord类,该类继承自LogRecord。我用JAX- B注释对此类进行了注释,包括,因此它将忽略未注释的字段和属性。当我启动tomcat时,出现错误,并且其他类没有默认的构造函数,但是没有带注释的方法对Level类或任何其他类进行任何引用。这些由父类引用。 我的子类具有需要定义的所有内容。如何使JAX-B完全忽略父类