如何解决:“您需要将XmlChoiceIdentifierAttribute添加到成员。”使用XmlAttributeOverrides时?

时间:2019-02-01 09:35:28

标签: c# xml serialization xml-serialization xmlserializer

我正在尝试使用XmlAttributeOverrides来更改将类序列化为XML的方式。我需要排除某些属性,并按特定顺序包含其他属性。

我在这里有此代码:

// XML Attribute Overrrides
public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, List<string> propertiesToInlcudeInOrder, List<string> allColumnNames)
{
    try
    {
        if (propertiesToInlcudeInOrder != null)
        {
            XmlAttributeOverrides theXMLAttributeOverrides = new XmlAttributeOverrides();
            if (propertiesToInlcudeInOrder.Count > 0)
            {
                XmlAttributes mainNewXMLAttributes = new XmlAttributes();
                mainNewXMLAttributes.XmlIgnore = false;

                XmlAttributes ignoreXMLAttributes = new XmlAttributes();
                ignoreXMLAttributes.XmlIgnore = true;

                List<string> propertiesToNotInclude = new List<string>();
                foreach (string theColumnName in allColumnNames)
                {
                    string thePropertyName = theColumnName;
                    bool addProperty = true;
                    foreach (string propertyToInclude in propertiesToInlcudeInOrder)
                    {
                        if (thePropertyName == propertyToInclude)
                        {
                            addProperty = false;
                            break;
                        }
                    }

                    if (addProperty)
                    {
                        propertiesToNotInclude.Add(thePropertyName);
                    }
                }

                // To Ignore
                foreach (string propertyNameToNotInlcude in propertiesToNotInclude)
                {
                    XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToNotInlcude);
                    theXMLElementAttributeToAdd.ElementName = propertyNameToNotInlcude;
                    ignoreXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);

                    theXMLAttributeOverrides.Add(theType, propertyNameToNotInlcude, ignoreXMLAttributes);
                }

                // To Add In Order
                int counter = 1;
                foreach (string propertyNameToIncludeInOrder in propertiesToInlcudeInOrder)
                {
                    XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToIncludeInOrder);
                    theXMLElementAttributeToAdd.ElementName = propertyNameToIncludeInOrder;
                    theXMLElementAttributeToAdd.Order = counter;
                    mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);

                    theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes);

                    counter++;
                }
            }

            return theXMLAttributeOverrides;
        }
        else
        {
            return null;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error at 'GetXMLAttributeOverrides'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        return null;
    }
}

在测试中,我有一个具有13个属性的类,我想按特定顺序包括3个属性,而排除所有其他属性。

我已确保列表中没有重复项。我已经仔细检查了“忽略列表”和“包含列表”中没有相同的属性名称。我在代码的这一行出现异常:XmlSerializer(dataToSerialize.GetType(), allXMLAttribueOverrides);

allXMLAttribueOverrides从我的方法GetXMLAttributeOverrides返回

例外是:

  

反映类型时出错   'System.Collections.Generic.List`1 [SystemName.UserControls.TestMain]'。

内部例外是:

  

反映属性'TextColumn'时出错。

     

您需要将XmlChoiceIdentifierAttribute添加到“ TextColumn”中   成员。

“ TextColumn”是测试类中的第一个属性

这是我的测试代码:

TestMain testItem = new TestMain(null, "TextColumnTEST", 5, Convert.ToDecimal(0.333), Convert.ToDecimal(0.777), DateTime.Now, "12:00:00", DateTime.Now, true, "Password", "#FFFFFF", null, null, null);
List<TestMain> dataToSerialize = new List<TestMain>();
dataToSerialize.Add(testItem);

List<string> propertiesToInlcudeInOrder = new List<string>();
propertiesToInlcudeInOrder.Add("CurrencyColumn");
propertiesToInlcudeInOrder.Add("NumberColumn");
propertiesToInlcudeInOrder.Add("TextColumn");

List<string> allColumnNames = new List<string>();
allColumnNames.Add("ID");
allColumnNames.Add("Select");
allColumnNames.Add("TextColumn");
allColumnNames.Add("NumberColumn");
allColumnNames.Add("CurrencyColumn");
allColumnNames.Add("DecimalColumn");
allColumnNames.Add("DateColumn");
allColumnNames.Add("TimeColumn");
allColumnNames.Add("DateAndTimeColumn");
allColumnNames.Add("YesNoColumn");
allColumnNames.Add("PasswordColumn");
allColumnNames.Add("ColorColumn");
allColumnNames.Add("ImageColumn");
allColumnNames.Add("DocumentColumn");
allColumnNames.Add("OtherColumn");

XmlAttributeOverrides allXMLAttribueOverrides = ReportingManipulation.GetXMLAttributeOverrides(dataToSerialize[0].GetType(), propertiesToInlcudeInOrder, allColumnNames);

using (StringWriter mainStringWriter = new StringWriter())
{
    XmlSerializer mainXMLSerializer = new XmlSerializer(dataToSerialize.GetType(), allXMLAttribueOverrides);
    mainXMLSerializer.Serialize(mainStringWriter, dataToSerialize);
    return mainStringWriter.ToString();
}

这是我的测试课:

public class TestMain
{
    #region Properties

    // Properties

    [XmlIgnore]
    public int? ID { get; set; }
    [XmlIgnore]
    public bool Select { get; set; }

    public string TextColumn { get; set; }
    public int NumberColumn { get; set; }
    public decimal CurrencyColumn { get; set; }
    public decimal DecimalColumn { get; set; }
    public DateTime DateColumn { get; set; }
    public string TimeColumn { get; set; }
    public DateTime DateAndTimeColumn { get; set; }
    public bool YesNoColumn { get; set; }
    public string PasswordColumn { get; set; }
    public string ColorColumn { get; set; }
    public byte[] ImageColumn { get; set; }
    public byte[] DocumentColumn { get; set; }
    public byte[] OtherColumn { get; set; }

    #endregion

    #region Constructors

    // Constructors
    public TestMain()
    {
        try
        {

        }
        catch (Exception ex)
        {
            MessageBox.Show("Error at Constructor: 'TestMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
    public TestMain(int? theID, string theTextColumn, int theNumberColumn, decimal theCurrencyColumn, decimal theDecimalColumn, DateTime theDateColumn, string theTimeColumn, DateTime theDateAndTimeColumn, bool theYesNoColumn, string thePasswordColumn, string theColorColumn, byte[] theImageColumn, byte[] theDocumentColumn, byte[] theOtherColumn)
    {
        try
        {
            this.ID = theID;

            this.TextColumn = theTextColumn;
            this.NumberColumn = theNumberColumn;
            this.CurrencyColumn = theCurrencyColumn;
            this.DecimalColumn = theDecimalColumn;
            this.DateColumn = theDateColumn;
            this.TimeColumn = theTimeColumn;
            this.DateAndTimeColumn = theDateAndTimeColumn;
            this.YesNoColumn = theYesNoColumn;
            this.PasswordColumn = thePasswordColumn;
            this.ColorColumn = theColorColumn;
            this.ImageColumn = theImageColumn;
            this.DocumentColumn = theDocumentColumn;
            this.OtherColumn = theOtherColumn;
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error at Constructor: 'TestMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }

    #endregion
}

我要去哪里错了?

任何帮助/建议将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的基本问题是您要为每个属性添加多个覆盖[XmlElement]属性,因为所有属性都使用单个实例mainNewXMLAttributes,累积为所有对象定义的XmlElementAttribute对象。

要解决此问题,您需要为mainNewXMLAttributes循环内的每个属性分配一个新的foreach (var propertyNameToIncludeInOrder in propertiesToInlcudeInOrder),如以下{{1 }}:

GetXMLAttributeOverrides()

为什么您的代码不起作用?在您的初始代码中,您执行以下操作:

public static partial class ReportingManipulation
{
    public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, IList<string> propertiesToInlcudeInOrder)
    {
        var allProperties = theType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).Select(p => p.Name);
        return GetXMLAttributeOverrides(theType, propertiesToInlcudeInOrder, allProperties);
    }

    // XML Attribute Overrrides
    public static XmlAttributeOverrides GetXMLAttributeOverrides(Type theType, IList<string> propertiesToInlcudeInOrder, IEnumerable<string> allProperties)
    {
        if (propertiesToInlcudeInOrder == null || propertiesToInlcudeInOrder.Count == 0)
            return null;

        var theXMLAttributeOverrides = new XmlAttributeOverrides();

        // To Add In Order
        int counter = 1;
        foreach (var propertyNameToIncludeInOrder in propertiesToInlcudeInOrder)
        {
            // Allocate a fresh instance of XmlAttributes for each property, because we are defining a different
            // XmlElementAttribute for each
            var mainNewXMLAttributes = new XmlAttributes { XmlIgnore = false };

            // Specify the element order XmlElementAttribute and attach to the XmlAttributes
            var theXMLElementAttributeToAdd = new XmlElementAttribute { Order = counter };
            mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd);

            // Attach the override XmlElementAttribute to the property propertyNameToIncludeInOrder
            theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes);

            counter++;
        }

        // To Ignore
        // Using System.Linq.Enumerable.Except()
        var propertiesToNotInclude = allProperties.Except(propertiesToInlcudeInOrder);
        var ignoreXMLAttributes = new XmlAttributes { XmlIgnore = true };
        foreach (var propertyNameToNotInlcude in propertiesToNotInclude)
        {
            // Attach the override XmlElementAttribute to the property propertyNameToIncludeInOrder
            // No need to allocate a fresh instance of ignoreXMLAttributes for each, because the instances would all be identical
            theXMLAttributeOverrides.Add(theType, propertyNameToNotInlcude, ignoreXMLAttributes);
        }

        return theXMLAttributeOverrides;
    }
}

现在,方法XmlAttributeOverrides.Add(Type, String, XmlAttributes)的工作方式如下:

  

XmlAttributes mainNewXMLAttributes = new XmlAttributes(); mainNewXMLAttributes.XmlIgnore = false; int counter = 1; foreach (string propertyNameToIncludeInOrder in propertiesToInlcudeInOrder) { XmlElementAttribute theXMLElementAttributeToAdd = new XmlElementAttribute(propertyNameToIncludeInOrder); theXMLElementAttributeToAdd.ElementName = propertyNameToIncludeInOrder; theXMLElementAttributeToAdd.Order = counter; mainNewXMLAttributes.XmlElements.Add(theXMLElementAttributeToAdd); theXMLAttributeOverrides.Add(theType, propertyNameToIncludeInOrder, mainNewXMLAttributes); counter++; } 对象添加到XmlAttributes对象的集合中。 XmlAttributes参数指定要覆盖的对象。 type参数指定被覆盖的成员的名称。

因此,member的内容将在最终构造mainNewXMLAttributes时应用于指定的参数。而且,当您仅为所有参数构造XmlSerializer的实例时,其mainNewXMLAttributes数组将包含与参数 all 对应的元素名称!即您的代码尝试将多个XmlElements属性应用于每个命名参数,仅在覆盖名称和顺序上有所不同。这说明您需要将XmlChoiceIdentifierAttribute添加到'TextColumn'成员。例外-如果属性值是多态的并且您想要分配不同的元素名称,则只能将多个元素名称附加到属性设置为不同的值类型。

注释

  • 在生成带有覆盖的[XmlElement]时,您必须静态地对其进行缓存,并在以后重用,以避免严重的内存泄漏,如 Memory Leak using StreamReader and XmlSerializer 中所述。 / p>

  • 我不建议无条件地吞下异常,并在低级实用程序方法或对象构造函数中将异常作为错误消息呈现给用户。

演示工作小提琴here