C# 如何动态选择要序列化的继承属性?

C# 如何动态选择要序列化的继承属性?,c#,serialization,C#,Serialization,我有一个带有几个属性的基类和三个派生类 我想序列化一个包含所有三个派生类的对象,但每个派生类都应该公开基类中不同的属性集 我想用XmlAttributeOverrides动态地实现这一点,并尝试了几种不同的方法来实现这一点,但没有真正做到这一点的方法 [Serializable] public class A { public string Property1 { get; set; } public string Property2 { get; set; } } [Seri

我有一个带有几个属性的基类和三个派生类

我想序列化一个包含所有三个派生类的对象,但每个派生类都应该公开基类中不同的属性集

我想用XmlAttributeOverrides动态地实现这一点,并尝试了几种不同的方法来实现这一点,但没有真正做到这一点的方法

[Serializable]
public class A
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
}

[Serializable]
public class B : A
{
}

[Serializable]
public class C : A
{
}

[Serializable]
public class Container
{
    public B B { get; set; }
    public C C { get; set; }
}


class Program
{
    static void Main(string[] args)
    {
        MemoryStream memoryStream = new MemoryStream();
        StreamWriter encodingWriter = new StreamWriter(memoryStream, Encoding.Unicode);

        var xmlWriter = XmlWriter.Create(encodingWriter, new XmlWriterSettings
        {
            Indent = false,
            OmitXmlDeclaration = true,
        });
        XmlAttributeOverrides overrides = new XmlAttributeOverrides();
        XmlAttributes attribute = new XmlAttributes();
        attribute.XmlIgnore = true;
        overrides.Add(typeof(B), "Property1", attribute);
        overrides.Add(typeof(C), "Property2", attribute);
        var container = new Container
            {
                B = new B {Property1 = "B property 1", Property2 = "B property 2"},
                C = new C {Property1 = "C property 1", Property2 = "C property 2"}
            };

        var xmlSerializer = new XmlSerializer(typeof(Container), overrides);
        xmlSerializer.Serialize(xmlWriter, container);

        var result = Encoding.Unicode.GetString(memoryStream.ToArray());
    }
}
在上面的代码中,结果字符串将包含B和C中A的所有属性,但我确实希望它只包含B Property2和C Property1(因为我已经为它们设置了XmlIgnore属性)

我该怎么做

编辑:所需的XML:

<Container xmlns:xsd="http://www.w3.org/2001/XMLSchema"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B><Property2>B property 2</Property2></B><C><Property1>C property 1</Property1></C></Container>
B属性2C属性1
实际的XML:

<Container xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><B><Property1>B property 1</Property1><Property2>B property 2</Property2></B><C><Property1>C property 1</Property1><Property2>C property 2</Property2></C></Container>
B属性1B属性2C属性1C属性2
编辑2:上面是一个将问题可视化的示例,但我将详细说明为什么我们需要这样做

我们有一个容器类(如上所述),它包含不同种类的派生对象(如上所述)。 当我们向其他人公开容器类中的数据时,我们希望能够只公开某些字段,这些字段可以在其他地方配置(可能是敏感数据或诸如此类)

我们在XmlAttributeOverrides的帮助下为公开的属性设置XmlIgnore属性。这适用于大多数类型的对象(没有继承),但现在我们需要以不同的方式序列化不同的派生对象(可配置)

因此,在上面的示例中,一些客户决定将Property1从类B中排除,将Property2从类C中排除,因此我希望XML与上面的一样。
但这不适用于上述代码;XmlSerializer似乎使用基类A的属性设置,而不是使用相应的派生类B和C的属性设置。

很难从您的问题中准确判断您想要的是什么XML输出,因此我只给出一个示例,您可以根据需要对其进行修改。(编辑:似乎我很幸运;下面的示例实现与您编辑的所需XML结果相匹配)

您可以使用鲜为人知的方法动态指示
XmlSerializer
忽略属性。例如:

public class A
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }

    public virtual bool ShouldSerializeProperty1()
    {
        return true;
    }

    public virtual bool ShouldSerializeProperty2()
    {
        return true;
    }
}
然后子类可以重写这些方法以忽略这些属性:

public class B : A
{
    public override bool ShouldSerializeProperty1()
    {
        return false;
    }
}

public class C : A
{
    public override bool ShouldSerializeProperty2()
    {
        return false;
    }
}
类似地,您可以通过
Container
指定的其他属性来控制方法的返回值:

public class A
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }

    [XmlIgnore]
    internal bool _ShouldSerializeProperty1 = true;
    [XmlIgnore]
    internal bool _ShouldSerializeProperty2 = true;

    public bool ShouldSerializeProperty1()
    {
        return _ShouldSerializeProperty1;
    }

    public bool ShouldSerializeProperty2()
    {
        return _ShouldSerializeProperty2;
    }
}
然后,将
B
C
分配给
容器时,可以设置这些标志:

public class Container
{
    private B _B;
    public B B 
    { 
        get
        {
            return _B;
        }
        set
        {
            if (value != null)
            {
                value._ShouldSerializeProperty1 = false;
                value._ShouldSerializeProperty2 = true;
            }
            _B = value;
        }
    }

    private C _C;
    public C C
    {
        get
        {
            return _C;
        }
        set
        {
            if (value != null)
            {
                value._ShouldSerializeProperty1 = true;
                value._ShouldSerializeProperty2 = false;
            }
            _C = value;
        }
    }
}
这些只是一些示例(我并不声称在这里使用了最佳实践)来演示如何使用
ShouldSerialize
。您可能会想对其进行调整,以使其最适合您的特定用途

编辑:对于更新后的帖子,还有另一种可能性,但需要额外定义子类和一些干冲突(尽管对于序列化,有时也可以)

首先将
A
的属性定义为
virtual
,并在子类中将它们作为基本包装器覆盖:

public class A
{
    public virtual string Property1 { get; set; }
    public virtual string Property2 { get; set; }
}

public class B : A
{
    public override string Property1 { get { return base.Property1; } set { base.Property1 = value; } }
    public override string Property2 { get { return base.Property2; } set { base.Property2 = value; } }
}

public class C : A
{
    public override string Property1 { get { return base.Property1; } set { base.Property1 = value; } }
    public override string Property2 { get { return base.Property2; } set { base.Property2 = value; } }
}
然后,由于(我假设)您正在通过这些配置来管理/构建
XmlSerializer
序列化,因此包括
XmlIgnore
对所有基类属性的重写:

public class B : A
{
    public override bool ShouldSerializeProperty1()
    {
        return false;
    }
}

public class C : A
{
    public override bool ShouldSerializeProperty2()
    {
        return false;
    }
}
然后还包括要真正忽略的子类属性的
XmlIgnore
覆盖:


这将生成您所需的输出。

虽然与您的问题无关,但请注意,在使用
XmlSerializer
时,
[Serializable]
属性不是必需的。我(出于其他原因)单独序列化了所有部分(通过反射)这在解决另一个问题的同时也解决了这个问题@ChrisSinclair有一个足够的解决方案,所以我把它标记为答案。我用预期的XML编辑了这个问题。我的感觉是应该有一个比用很多特定的ShouldSerialize属性装饰更简单的解决方案。提供的代码就是一个抓住问题的例子,我的实际代码使用了更多的带有反射和其他内容的通用代码来确定哪些属性应该序列化而不是。@Joel:嗯,您所做的通常不在XML序列化的常见用例场景中,所以这“不容易”也就不足为奇了。理想情况下,您希望您的数据模型是固定的,并且让您的C#类与您的XML模式一一匹配。您的应用程序代码应该从XML序列化代码中抽象出来,这样您就不必执行这样的愚蠢操作。(是的,这可能意味着您可能有一个特定的
Container_B
Container_C
类,似乎没有从通用的
a
类派生。然后在处理应用程序逻辑时转换为这些类或从这些类转换而来。)如果您使用反射和“stuff”来确定应该序列化哪些属性,也许很难对你的问题的最佳解决方案发表评论。我能给出的最好建议(甚至可能不适用)是尽可能从序列化问题中抽象出应用程序逻辑。保持数据序列化层的简单,否则在尝试更改/更新架构时只会导致头痛(如现在)或以后。我在原始帖子中添加了额外的信息,希望能更好地描述这种需要,如果需要,我将继续提供更多信息。:)@乔尔:我得到了一张更好的照片。不幸的是,几年前我也尝试过使用继承来控制属性在XML中的格式。老实说,我不记得具体细节(如果它们用于重写或隐藏,或者使用基于基类或子类的
xmlserializer
等等),但我得出的结论是避免这样做。然而,我破解了一个绕过它的方法,所以你可以把它拉下来,请参阅我的编辑
overrides.Add(typeof(B), "Property2", attribute);
overrides.Add(typeof(C), "Property1", attribute);