简体   繁体   English

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

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

I have a base class with a few properties, and three deriving classes. 我有一个基类,有一些属性和三个派生类。

I want to serialize an object containing all three derived classes, but each derived class should expose a different set of properties from base class. 我想序列化包含所有三个派生类的对象,但每个派生类应该从基类公开一组不同的属性。

I want to do this dynamically with XmlAttributeOverrides and have tried a few differnt ways to do that, but nothing that really does it. 我想用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());
    }
}

In the above code, the result string will contain all the properties of A in B and C, but I really want it just to contain B Property2 and C Property1 (as I have set the XmlIgnore attributes for them). 在上面的代码中,结果字符串将包含B和C中A的所有属性,但我真的希望它只包含B Property2和C Property1(因为我为它们设置了XmlIgnore属性)。

How do I do this? 我该怎么做呢?

EDIT: The expected XML: 编辑:预期的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>

The actual XML: 实际的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>

EDIT 2: The above is an example to visualize the problem, but I will expand on why we have the need to do this. 编辑2:上面是一个可视化问题的示例,但我将扩展为什么我们需要这样做。

We have a Container-class (like above) which contains different kinds of derived objects (like above). 我们有一个Container类(如上所述),它包含不同类型的派生对象(如上所述)。 When we expose data from the Container-class to others, we want to be able to expose just certain fields which are configurable at some other place (might be sensitive data or whatnot). 当我们将Container类中的数据公开给其他人时,我们希望能够公开某些可在其他地方配置的字段(可能是敏感数据或诸如此类的东西)。

This we do with help of XmlAttributeOverrides to set XmlIgnore property for the exposed properties. 我们在XmlAttributeOverrides的帮助下为暴露的属性设置XmlIgnore属性。 This works well for most types of objects (which does not have inheritance), but now we have the need to serialize different derived objects in different ways (configurable). 这适用于大多数类型的对象(没有继承),但现在我们需要以不同的方式(可配置)序列化不同的派生对象。

So in the above example, some customer has made the decision to exclude Property1 from class B and Property2 from class C, and as such I want the XML to look like above. 因此,在上面的示例中,一些客户已决定从类C中排除Property1,从C类中排除Property2,因此我希望XML看起来如上所示。 This did not work with the above code though; 但这不适用于上述代码; it seems like XmlSerializer uses the settings for the properties from the base class A, instead of using it from the respective derived classes B and C. 似乎XmlSerializer使用来自基类A的属性的设置,而不是从相应的派生类B和C中使用它。

It's hard to tell from your question exactly what XML output you're looking for, so I'll just throw out an example and you can modify it as you need to. 很难从你的问题中确切地知道你正在寻找什么XML输出,所以我只是抛出一个例子,你可以根据需要修改它。 (EDIT: Seems I lucked out; the sample implementations below match your edited desired XML result) (编辑:似乎我很幸运;下面的示例实现与您编辑的所需XML结果相匹配)

You can use the little known ShouldSerializePROPERTYNAME method to dynamically instruct the XmlSerializer to ignore properties. 您可以使用鲜为人知的ShouldSerializePROPERTYNAME方法动态指示XmlSerializer忽略属性。 For example: 例如:

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;
    }
}

These methods can then be overridden by the subclasses to ignore those properties: 然后子类可以重写这些方法以忽略这些属性:

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

public class C : A
{
    public override bool ShouldSerializeProperty2()
    {
        return false;
    }
}

Similarly, you can control the return values of the methods via other properties which can be assigned by Container : 同样,您可以通过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;
    }
}

Then when assigning B and C to Container , you can set those flags: 然后在将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;
        }
    }
}

These are just some examples (and I don't claim to have used best practices here) to demonstrate how ShouldSerialize can be used. 这些只是一些例子(我并未声称在这里使用了最佳实践)来演示如何使用ShouldSerialize You'll probably want to adapt it however best to your particular usage. 您可能希望最好地适应您的特定用途。

EDIT: Given your updated post, there's another possibility but requires a bit of extra work defining your subclasses and some DRY violation (though for serialization, sometimes that's ok). 编辑:鉴于你的更新帖子,还有另一种可能性,但需要一些额外的工作来定义你的子类和一些DRY违规(虽然对于序列化,有时候没问题)。

First define A 's properties as virtual and override them in the subclasses as basic wrappers: 首先将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; } }
}

Then since (I'm assuming) you're managing/building your XmlSerializer serialization via those configurations, include XmlIgnore overrides for all of the base class's properties : 然后,因为(我假设)您正在通过这些配置管理/构建XmlSerializer序列化,所以包括所有 基类属性的 XmlIgnore覆盖:

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

Then also include XmlIgnore overrides for the subclass properties that you wish to truly ignore: 然后包括您希望真正忽略的子类属性的XmlIgnore覆盖:

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

This will produce your desired output. 这将产生您想要的输出。

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

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