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

标签 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

返回

异常(exception)情况是:

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”是我的测试类中的第一个属性

这是我的测试代码:

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
}

我哪里出错了?

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

最佳答案

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

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

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

注释

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

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

演示工作 fiddle here .

关于c# - 如何解决: "You need to add XmlChoiceIdentifierAttribute to the member." when using XmlAttributeOverrides?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54476647/

相关文章:

c# - DateTime.Now/UtcNow 默认文化 - 客户端还是服务器?

c# - 如何在 wpf 中获取多个项目的边界框?

GWT RPC 警告检查所有子类型

python - 使用 python,除一个元素外,按字母顺序对 XML 进行排序

xml - 使用 XSLT 1.0 将 XML 元素根据某些条件按顺序分组到桶中

c# - XmlSerializer 是否会转义 & 等特殊字符?

c# - Json.NET StringEnumConverter 未按预期工作

c# - 将 Lambda 表达式声明为类常量字段

c# - 如何修复此错误?无效方差 : The type parameter 'T' must be invariantly valid on

php - 具有多个参数的 SOAP XML 函数调用