【问题标题】:Base Class Serialization Issue - do not serialize Derived Class Properties基类序列化问题 - 不要序列化派生类属性
【发布时间】:2021-02-12 13:31:01
【问题描述】:

我厌倦了序列化相当大的实体列表,这些实体都是从基类派生的。 我只需要客户端中的基类属性。如何在不实例化基类的新实例的情况下实现这一点?

我尝试创建自定义 ContractResolver,但它似乎在运行时执行 getType(),而不是使用正在序列化的列表/数组的类型

请参阅下面的代码示例。

我想实现。 castBaseString == actualBaseString ;

所以我希望 castBaseString = [{"Id":1},{"Id":2}] 而不是 [{"Value":"value","Id":1},{"Value":"value2","Id":2}]

using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using Newtonsoft.Json;


namespace Tests {

    [TestClass]
    public class JsonNetTest {
        class Base {
            public int Id { get; set; }
        }

        class Derived : Base {
            public string Value { get; set; }
        }
        
        class OtherDerived : Base {
            public string Value { get; set; }
            public string OtherValue { get; set; }
        }

        [TestMethod]
        public void Test() {
            IEnumerable<Derived> deriveds = new Derived[] {
                new Derived {Id = 1, Value = "value" },
                new Derived {Id = 2, Value = "value2" }
            };

            IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();

            IEnumerable<Base> bases = new Base[] {
                new Base {Id = 1 },
                new Base {Id = 2 }
            };
            JsonSerializerSettings s = new JsonSerializerSettings();

            var derString = JsonConvert.SerializeObject(deriveds, s);
            var castBaseString = JsonConvert.SerializeObject(castBases, s);
            var actualBaseString = JsonConvert.SerializeObject(bases, s);

            Assert.AreEqual(actualBaseString, castBaseString);
            Assert.AreNotEqual(castBaseString, derString);
        }
    }
}

根据评论进行编辑 附加上下文:
为了清楚起见,我只是发布了这个简单的测试用例。 正在使用的实际上下文是在 aspnet 核心应用程序中。

假设有 3 个控制器

  1. /api/DerivedController/
  2. /api/OtherDerivedController/
  3. /api/BaseController/

当客户端调用 1 时,我们希望返回 Derived 列表,当客户端调用 2 我们想在他们调用时返回一个 OtherDerived 的列表 3 我们想返回一个 Base

的列表

数据存储在数据库 TBL_DERIVED 和 TBL_OTHERDERIVED 的 2 个不同的表中。

当他们调用 base 时,我们想要实现的是从这些表中的一个或两个返回数据,而只是这些表的公共属性。

希望这可以澄清。

【问题讨论】:

    标签: c# .net json.net


    【解决方案1】:

    如果您不想使用属性,可以使用 ContractResolver 强制仅对来自 Base 的属性进行序列化:

    public class DerivedTypeFilterContractResolver<T> : DefaultContractResolver {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
            JsonProperty property = base.CreateProperty(member, memberSerialization);
            if (property.DeclaringType != typeof(T)) {
                property.ShouldSerialize = instance => false;
            }
            
            return property;
        }
    }
    

    然后像这样使用它:

    void Main() {
    
        IEnumerable<Derived> deriveds = new Derived[] {
                    new Derived {Id = 1, Value = "value" },
                    new Derived {Id = 2, Value = "value2" }
                };
    
        IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
    
        IEnumerable<Base> bases = new Base[] {
                    new Base {Id = 1 },
                    new Base {Id = 2 }
                };
    
        JsonSerializerSettings s = new JsonSerializerSettings {
            ContractResolver = new DerivedTypeFilterContractResolver<Base>()
        };
    
        var derString = JsonConvert.SerializeObject(deriveds, s);
        var castBaseString = JsonConvert.SerializeObject(castBases, s);
        var actualBaseString = JsonConvert.SerializeObject(bases, s);
    
        Console.WriteLine(castBaseString);
    }
    
    
    class Base {
        public int Id { get; set; }
    }
    
    class Derived : Base {
        public string Value { get; set; }
    }
    

    输出:

    [{"Id":1},{"Id":2}]
    

    【讨论】:

    • 这一切都很好,但是现在第二个断言失败了,因为在序列化时,Derived 将它们序列化为 '[{"Id":1},{"Id":2}]' ,省略 Derived.Value 字段。
    • 另外,如果您现在序列化任何其他不是从 Base 派生的对象,它们都将是 '[{},{}]'
    • 嗯,是的。当您只需要基类值时,您只会使用此解析器。
    • 关于如何在 aspnet 核心项目中执行此操作的任何想法。我很确定合约解析器在启动时被缓存,并且出于性能原因你不能在运行时更改它。我会看看这样的事情是否可行:dotnetcoretutorials.com/2018/05/05/…
    • 对如何在 aspnet 核心项目中执行此操作有任何想法吗? - 您必须提供更多详细信息。您问 我正在尝试序列化相当大的实体列表,这些实体都派生自基类。我只需要客户端中的基类属性。 使用这个合约解析器就可以做到这一点。但现在你是说你只想“有时”这样做?到底是哪个时间?而且您肯定可以控制序列化过程,并且可以为所欲为吗?
    【解决方案2】:

    在属性顶部添加[JsonIgnore]。

    class Derived : Base {
        [JsonIgnore]
        public string Value { get; set; }
    }
    

    【讨论】:

    • 另一种选择是使用[JsonObject(MemberSerialization = MemberSerialization.OptIn)] 属性Derived 类以默认排除所有属性(无需使用JsonIgnoreAttribute 标记它们)。
    • 对于其他特定的调用,我确实想序列化值,但不是在序列化基类时。
    • 另外,我没有选项可以将属性添加到我正在使用的类型中,因为它是第三方数据模型。
    【解决方案3】:

    事实证明,json 序列化的工作方式是不可能的。 @stuartd 回答可能是一个很好的解决方法,如果您只有有限的情况想要这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      • 2019-05-10
      • 1970-01-01
      相关资源
      最近更新 更多