如何解决:"You need to add XmlChoiceIdentifierAttribute to the member." when using XmlAttributeOverrides?

How to solve: "You need to add XmlChoiceIdentifierAttribute to the member." when using XmlAttributeOverrides?

我正在尝试使用 XmlAttributeOverrides 将我的 class 序列化为 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 个属性的 class,我想按特定顺序包含 3 个并排除所有其他属性。

我已经确保我的列表中没有任何重复项。我仔细检查了我在 'Ignore List' 和 'Include List' 中没有相同的 属性 名称。我的这行代码出现异常:XmlSerializer(dataToSerialize.GetType(), allXMLAttribueOverrides);

allXMLAttribueOverrides 是从我的方法返回的 GetXMLAttributeOverrides

例外情况是:

There was an error reflecting type 'System.Collections.Generic.List`1[SystemName.UserControls.TestMain]'.

内部异常是:

There was an error reflecting property 'TextColumn'.

You need to add XmlChoiceIdentifierAttribute to the 'TextColumn' member.

'TextColumn'是我测试的第一个属性class

这是我的测试代码:

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

这是我的测试 class:

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
}

我哪里错了?

如有任何帮助/建议,我们将不胜感激。

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

要解决此问题,您需要为 foreach (var propertyNameToIncludeInOrder in propertiesToInlcudeInOrder) 循环内的每个 属性 分配一个新的 mainNewXMLAttributes,如以下 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;
    }
}

为什么你的代码不起作用? 在你的初始代码中,你做了:

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

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

Adds an XmlAttributes object to the collection of XmlAttributes objects. The type parameter specifies an object to be overridden. The member parameter specifies the name of a member that is overridden.

因此 mainNewXMLAttributes 的内容将在最终构造 XmlSerializer 时应用到命名参数。而当你只为所有参数构造 mainNewXMLAttributes 的实例时,它的 XmlElements 数组将包含与参数的 all 对应的元素名称! IE。您的代码尝试将多个 [XmlElement] 属性应用于每个命名参数,仅在覆盖名称和顺序上有所不同。这说明了 您需要将 XmlChoiceIdentifierAttribute 添加到 'TextColumn' 成员。 异常 - 您只能将多个元素名称附加到 属性 如果 属性 value 是多态的,你想将不同的元素名称分配给不同的值类型。

备注

  • 生成带有覆盖的 XmlSerializer 时,您必须静态缓存它并在以后重用它以避免严重的内存泄漏,如 Memory Leak using StreamReader and XmlSerializer 中所述.

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

演示工作 fiddle here.