如何根据父 class 类型有选择地从序列化中排除属性

How to selectively exclude a property from serialization based on the parent class type

我正在使用 Newtonsoft.JSON 库序列化多个对象。在某些情况下,我不想序列化一个属性,所以我使用了 ShouldSerialize 前缀,这在大多数情况下都非常成功。在一种情况下,我只想序列化属于特定 class 的属性。

我试过使用堆栈跟踪,但它只告诉我 JSON 对象正在调用 ShouldSerialize 方法。我不需要知道什么调用 ShouldSerialize,我需要知道父 class ShouldSerialize 属于什么,例如 Parent.Child.ShouldSerialize.

在使用下面的代码示例使用 JSON 对象时,如何确定父 class 名称是什么?

class Foo
{
    public SharedClass SomeProperty
    {
           get;
           set;
    }
}

class Bar
{
    public SharedClass SomeProperty
    {
           get;
           set;
    }
}

class SharedClass
{
    public string SomeValue
    {
           get;
           set;
    }

    public bool ShouldSerializeSomeValue
    {
           //pseudo logic
           return ClassName == "Foo";
    }
}

正如 Lasse Karlsen 在评论中所指出的那样,如果您的 SharedClass 没有对其父级的引用,那么 [=52= 中的 ShouldSerializeSomeValue() 方法就没有办法了] 知道父级 class 是什么。

但是,如果您使用的是 Json.Net 6.0 Release 6 或更高版本,您可以通过使用自定义 JsonConverter 作为一种有选择地从共享 class 中省略属性的方法来解决这个问题(而不是使用 ShouldSerialize() 方法),然后将 [JsonConverter] 属性放在适当父 class 中的 SharedClass 属性上,以指示该实例应省略哪些属性。

更新后的示例 class 定义可能如下所示。您会注意到我在 Foo 上标记了 SharedClass 实例,以指示它应该使用名为 OmitPropertiesConverter 的自定义转换器来省略 SomeValue 属性。 Bar 上的 SharedClass 实例不使用转换器,因此该实例将正常序列化。

class Foo
{
    [JsonConverter(typeof(OmitPropertiesConverter), "SomeValue")]
    public SharedClass Shared { get; set; }
}

class Bar
{
    public SharedClass Shared { get; set; }
}

class SharedClass
{
    public string SomeValue { get; set; }
    public string SomeOtherValue { get; set; }
}

下面是 OmitPropertiesConverter 的代码。它的构造函数接受一个 propsToOmit 字符串,该字符串是要从序列化中排除的 属性 名称的逗号分隔列表。这被拆分成一个数组,供以后在 WriteJson 方法中使用。 WriteJson 方法采用 SharedClass 值,将其转换为 JObject,然后在将 JObject 写入之前以编程方式删除 propsToOmit 数组中的属性JsonWriter.

class OmitPropertiesConverter : JsonConverter
{
    string[] propsToOmit;

    public OmitPropertiesConverter(string propsToOmit)
    {
        this.propsToOmit = propsToOmit.Split(new char[] {','},
                                             StringSplitOptions.RemoveEmptyEntries);
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(SharedClass));
    }

    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
    {
        JObject jo = JObject.FromObject(value, serializer);

        // Note: ToList() is needed here to prevent "collection was modified" error
        foreach (JProperty prop in jo.Properties()
                                     .Where(p => propsToOmit.Contains(p.Name))
                                     .ToList())
        {
            prop.Remove();
        }

        jo.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType,
                                    object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

这是一个简单的演示程序,显示了转换器的运行情况:

class Program
{
    static void Main(string[] args)
    {
        var root = new
        {
            Foo = new Foo
            {
                Shared = new SharedClass
                {
                    SomeValue = "foo1",
                    SomeOtherValue = "foo2"
                }
            },
            Bar = new Bar
            {
                Shared = new SharedClass
                {
                    SomeValue = "bar1",
                    SomeOtherValue = "bar2"
                }
            }
        };

        string json = JsonConvert.SerializeObject(root, Formatting.Indented);
        Console.WriteLine(json);
    }
}

这是上述演示的输出。您会注意到 Foo 内的 SharedClass 实例上的 SomeValue 属性 未包含在输出中,但它包含在 Bar 内的实例中.

{
  "Foo": {
    "Shared": {
      "SomeOtherValue": "foo2"
    }
  },
  "Bar": {
    "Shared": {
      "SomeValue": "bar1",
      "SomeOtherValue": "bar2"
    }
  }
}