繁体   English   中英

如何动态选择将序列化继承的属性?

[英]How to dynamically choose what inherited properties will be serialized?

我有一个基类,有一些属性和三个派生类。

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

我想用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>

实际的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>

编辑2:上面是一个可视化问题的示例,但我将扩展为什么我们需要这样做。

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

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

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

很难从你的问题中确切地知道你正在寻找什么XML输出,所以我只是抛出一个例子,你可以根据需要修改它。 (编辑:似乎我很幸运;下面的示例实现与您编辑的所需XML结果相匹配)

您可以使用鲜为人知的ShouldSerializePROPERTYNAME方法动态指示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;
    }
}

然后在将BC分配给Container ,您可以设置这些标志:

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 您可能希望最好地适应您的特定用途。

编辑:鉴于你的更新帖子,还有另一种可能性,但需要一些额外的工作来定义你的子类和一些DRY违规(虽然对于序列化,有时候没问题)。

首先将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覆盖:

overrides.Add(typeof(A), "Property1", attribute);
overrides.Add(typeof(A), "Property2", attribute);

然后包括您希望真正忽略的子类属性的XmlIgnore覆盖:

overrides.Add(typeof(B), "Property2", attribute);
overrides.Add(typeof(C), "Property1", attribute);

这将产生您想要的输出。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM