在聚合某个类的另一个类的序列化过程中排除该类的某些属性

本文关键字:排除 属性 过程中 另一个 序列化 | 更新日期: 2023-09-27 18:28:02

我正在使用XmlSerializer。一个大的类实例正在被序列化,而这个大实例聚合(我想说几乎拥有)较小的类,并涉及一些继承。在基类中,一些属性似乎是至关重要的,并且在序列化时用于镜像它们的状态。但对于它们的一些子类,这些属性应该被抑制,因为添加了其他更可读的属性。

下面是一个示例(不是真实的代码示例):

class Base
{
    public int x { get; set; }
    // ...other stuff...
}
class Derived1 : Base
{
    // just some added functions
    // it's perfectly fine serializing base.x
    // ...other stuff...
}
class Derived2 : Base
{
    public int y { get; set; } // will in the end be populating this.y 
    // and base.x, but is more readable and has more sense in XML
    // ...other stuff...
}
class BigClass
{
    public List<Derived1> List1 { get { return m_List1; } }
    private List<Derived1> m_List1 = new List<Derived1>();
    public List<Derived2> List2 { get { return m_List2; } }
    private List<Derived2> m_List2 = new List<Derived2>();
    // ...other stuff...
}

我读这篇文章是为了了解使用XmlAttributeOverrides的主题。显然,它适用于XmlSerializer s = new XmlSerializer(typeof(Derived2), overrides)这样的情况。然而,当我尝试将其与BigClass:一起使用时

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes ignore = new XmlAttributes { XmlIgnore = true };
overrides.Add(typeof(Derived2), "x", ignore);
XmlSerializer serializer = new XmlSerializer(typeof(BigClass), overrides);
// and, finally, save it
using (TextWriter tw = new StreamWriter("output.xml"))
{
    serializer.Serialize(tw, SomeBigClassInstance);
}

输出仍然包含两种类型的CCD_ 4属性。List通过引用适当的子类来包含它们,所以XmlSerializer并不能看到它们的实际类。尽管有覆盖,但示例xml输出看起来像

<?xml version="1.0" encoding="utf-8"?>
<BigClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <List1>
    <Derived1>
      <x>1</x>
      <!-- other stuff -->
    </Derived1>
  </List1>
  <List2>
    <Derived2>
      <x>2</x>  <!-- unwanted guy -->
      <y>20</y>
      <!-- other stuff -->
    </Derived2>
  </List2>
  <!-- other stuff -->
</BigClass>

所以问题是:我在这里做错了什么?这种抑制不需要的属性的方法是否只有在序列化程序故意仅应用于此类的节点时才有效?

注意。在我的真实案例中,节省的钱不止一行。有更多的属性可以像这样隐藏,列表可以包含相当多的项目。关注的是可读性,以及在某种程度上,所生成的xml文件的手动可修改性。显然,重新计算x属性或手动删除它们对于尝试这样的努力来说已经够累的了。

更新1。尝试添加类似的代码

overrides.Add(typeof(List<Derived2>), "x", ignore);

似乎并没有解决这个问题。实际上,在聚合层次结构中,从BigClassDerived1/2还有更多的节点。将它们从Derived2添加到根并没有帮助。因此,我假设即使在这种简化的情况下,这个提案也不会解决它。(更新2。不会)。

在聚合某个类的另一个类的序列化过程中排除该类的某些属性

(免责声明。有些人可能会说,序列化字段就像有问题的类的公共接口,所以将它们隐藏在派生类中有点像代码味。但我不打算在这里讨论接口含义,只讨论实现上述目标的方式。)

XmlSerializer有两个特性,允许从输出文件中排除一些属性:

  1. <field>Specified模式(又称旧模式),例如:如何使用.NET XmlSerializer使值类型可以为null
  2. ShouldSerialize<field>()模式,例如:Xml序列化-隐藏空值

在从继承树中删除冗余字段方面,两者都有一些优点和缺点。让我们看看:

CCD_ 14模式。用法:

public class Example
{
    public int x { get; set; }
    [XmlIgnore]
    public bool xSpecified;
    public Example()
    {
        xSpecified = <set your value for this class>;
    }
}

优点:

  • 如果逻辑需要,可以在其他类中设置

缺点:

  • 破坏有问题的类的封装
  • 贡献内存中对象的大小,每个属性每个对象一个布尔值
  • 你必须记住将它设置为合适的值,例如在构造函数中

CCD_ 15模式。用法:

public class Example
{
    public int x { get; set; }
    public virtual bool ShouldSerializex()
    {
        return <your logic expression here>;
    }
}

优点:

  • 保留类的封装,允许逻辑包含在其中
  • 每个属性没有额外的内存
  • 整体更灵活

缺点:

  • 在这种情况下,必须使它成为虚拟的,这样它也可以与派生类一起工作,因此可能会为每个对象中的vtable指针增加额外的内存

对于我的具体问题,我在不同的地方使用了这两种方法。具有几个需要隐藏的属性的自给自足的类从#2中获得了更多优势,尤其是如果项目中有一个又一个列表,其中有很多实例。小类曾经是组成大类的"砖砌石",可能会"愚蠢"到不知道是否序列化一些东西,这使得#1成为一个可能的选择。