在聚合它的另一个类的序列化期间排除一个类的一些属性

时间:2015-12-24 13:02:39

标签: c# .net xml

我正在使用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...
}

我已阅读this了解使用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);
}

输出仍包含两种类型的x属性。 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.它不会)。

1 个答案:

答案 0 :(得分:1)

  

(免责声明。有些人可能会说序列化字段就像所讨论的类的公共接口一样,所以将它们隐藏在派生类中是一种代码味道。但我不打算在这里解决接口问题,只有达到目标的目的。)

XmlSerializer有两个功能允许从输出文件中排除某些属性:

  1. <field>Specified模式(AKA旧方式),例如就像在这里:How to make a value type nullable with .NET XmlSerializer?
  2. ShouldSerialize<field>()模式,例如像这里:Xml serialization - Hide null values
  3. 在继承树中删除冗余字段时,两者都有一些优点和缺点。我们来看看:

    <field>Specified模式。用法:

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

    优点:

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

    缺点:

    • 打破有问题的课程的封装
    • 在内存中贡献对象的大小,每个属性每个对象一个布尔值
    • 您必须记住将其设置为适当的值,例如在构造函数中

    ShouldSerialize<field>()模式。用法:

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

    优点:

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

    缺点:

    • 在这种情况下,必须将其设置为虚拟,以便它也适用于派生类,因此可能为每个对象中的vtable指针添加额外内存

    对于我的特定问题,我在不同的地方使用了两种方法。具有多个需要隐藏的属性的自足类从#2中获得更多优势,特别是如果项目在其大量实例的列表上有列表时。过去的小班是砖石和#34;为了组成更大的那些可能是“愚蠢的”#34;足以不知道是否序列化某些东西,这使得#1成为可能的选择。