【问题标题】:Newtonsoft.json or System.Text.Json Serialize only the property's defined type not inherited valuesNewtonsoft.json 或 System.Text.Json 仅序列化属性的定义类型而不是继承值
【发布时间】:2021-06-23 16:04:23
【问题描述】:

假设你有:

class ContentRef {
  SomeProperty
}

class Content : ContentRef {
   SomeOtherProperty
}

class C  {
   ContentRef someProperty;
   List<ContentRef> someList;
}

var someObject = new C { someProperty = new Content(), someList = new List<ContentRef>({new Content()})};

默认情况下,Newtonsoft 和 System.Text.Json 都会序列化 someObject 中 Content 的所有属性。但很明显,C 的定义说两者都是 A 类型。我想确保在直接属性 A 和 List 的情况下,即使您将 B 分配给它,唯一被序列化的是 A 的属性。

当您为正在设置的属性设置一个 Ref 类并将其设置为从其余数据的 ref 类继承的完整类时,这种情况在文档数据库中一直发生。您只希望文档数据库存储 ref 类的数据,而不是整个类的所有数据,因为当它被反序列化时,您只关心定义的 Ref 类。

我有这个:

public class StrictTypeContractResolver : DefaultContractResolver
{
    private readonly FieldInfo _IsSealedField = typeof(JsonContract).GetField("IsSealed", BindingFlags.Instance | BindingFlags.NonPublic)!;

    public override JsonContract ResolveContract(Type type)
    {
        var resolveContract = base.ResolveContract(type);
        _IsSealedField.SetValue(resolveContract, true);
        return resolveContract;
    }
}

这适用于直接属性情况,仅序列化实际定义的类型。但是,我不知道如何让它在 List 类型(哈希集、IList 或 IEnumerable)上工作。它序列化整个事物,而不是像它应该的那样序列化 List。

【问题讨论】:

  • 有些合约解析器会忽略基本类型成员 herehere。您可以检查这些是否适合您,如果不适合,请分享minimal reproducible example 吗?您的 ContentRef 示例似乎是伪代码,因此我不清楚您在哪里遇到问题。
  • 另外,为什么同时标记system.text.jsonjson.netSystem.Text.Json doesn't even have a publicly facing contract resolver 所以答案会完全不同。
  • 这不是我想忽略的基本类型。当属性或列表元素类型是基本类型时,我想忽略继承的类型属性,并且应该将其反序列化为属性的定义类型,而不是传入的任何内容。它就像设置定义为 ContentRef 的属性一样简单内容将导致它与所有内容而不是 ContentRef 一起序列化,并且它应该忽略其余的并且只有序列化 ContentRef。更糟糕的是,即使类型已明确定义,它也会对列表元素执行此操作。所以我正在尝试撤消这种荒谬的行为。
  • Ps:json.net 和 System.Text.Json 都表现出相同的荒谬行为,并忽略您的类型定义并序列化子类型而不是定义的基类型。
  • 我认为自动序列化派生类型的属性一点也不荒谬。事实上,它是首选。假设您有一个包含形状列表的 Canvas 类。基本形状在画布上具有名称属性和位置。但是每个衍生形状都有定义它的特定属性——例如,圆形有一个中心和半径,而多边形有一系列顶点。如果我要序列化 ​​Canvas,我绝对希望形状的细节也被序列化,否则我将无法从 JSON 重建完整的 Canvas!

标签: c# json.net system.text.json


【解决方案1】:

https://dotnetfiddle.net/CCbOFf

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using static Newtonsoft.Json.JsonConvert;

class Ref { public int a {get;set;} public string b {get;set;} public DateTime c {get;set;} }
class DerivedRef : Ref { public bool d {get;set;} public long e {get;set;} }

class Ref2 { public int a {get;set;} public string b {get;set;} public DateTime c {get;set;} }
class DerivedRef2 : Ref2 { public bool d {get;set;} public long e {get;set;} }

public class StrictTypeContractResolver<T>: DefaultContractResolver
{
    
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        if(type.IsSubclassOf(typeof(T)))
        {           
            return base.CreateProperties(typeof(T), memberSerialization);
        }
        
        return base.CreateProperties(type, memberSerialization);
    }
    
}

public class Program
{
    public static void Main()
    {       
        var list = new Ref[]{
            new Ref{a=1,b="2",c=DateTime.Now}
            ,new DerivedRef{a=1,b="2",c=DateTime.Now,d=true,e=999}
        };

        Console.WriteLine(PerformSerialization(list));
            
        var list2 = new Ref2[]{
            new Ref2{a=1,b="2",c=DateTime.Now}
            ,new DerivedRef2{a=1,b="2",c=DateTime.Now,d=true,e=999}
        };

        Console.WriteLine(PerformSerialization(list2));
    }
    
    public static string PerformSerialization<T>(IList<T> list)
    {
        var settings = new JsonSerializerSettings { ContractResolver = new StrictTypeContractResolver<T>(), Formatting = Formatting.Indented  };
        return SerializeObject(list, settings);
    }
    
    
}

【讨论】:

  • 问题是我必须维护方法中每个人的列表。而不是它只是智能地工作。这必须是可能的,而不必诉诸于此。
  • 所以你说它总是一个 DerivedRef : Ref 关系,你希望它只接受 Ref,不管 Ref 类型是什么?
  • 不,我有几十个这样的例子,并且随着框架的增长而添加更多,并且必须记住维护这是一个错误的秘诀,我不希望完整的对象保留在文档存储中,只是引用,并且几乎总是将完整的对象分配给基本类型,因为您将加载整个对象。并且必须记住强制 castor 或 map 等待发生的错误更多是显而易见的:完全保留定义的类型并忽略任何继承的东西。
  • 听起来你想要一个通用的 T 解决方案。
  • Generic T 仍然需要定义它们中的每一个。我希望默认行为完全按照定义进行序列化,并允许具有在这种罕见情况下设置的属性的多态性异常。我正在开发一个自定义转换器,但 ravendb 忽略了它,所以解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-14
  • 2022-06-27
  • 2021-12-09
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多