xml 反序列化产生的对象只有 1 个元素,尽管 xml 有多个元素

Object resulting from xml deserialization only has 1 element, despite xml having multiple elements

我正在尝试测试网络请求的序列化。 我正在做一个单元测试,我: - 从服务器创建一个模拟响应 - 反序列化该响应 - 将初始对象与反序列化对象进行比较

问题是我的数组之一只填充了部分元素,而不是所有元素,它只有一个,最后一个。 由于 xml 架构限制,必须手动进行反序列化。 项目是部分 class 以将 DTO 与 xml 操作分开

我已经尝试将数组 属性 的属性更改为

[XmlArray("items")]
[XmlArrayItemAttribute("item")]

我只测试了单个项目的序列化-反序列化并且它有效。

我检查了原始序列化产生的 xml 和反序列化后的 xml,它们是相等的。

首先是项目本身:

[XmlRoot("item")]
public partial class InvoiceItem
{      
    [XmlElement(ElementName = "name")]
    public string Name { get; set; }    
}

现在数组:

    [XmlArray("items")]
    [XmlArrayItemAttribute("item")]
    public InvoiceItem[] InvoiceItems
    {
        get
        {
            return this.invoiceItems;
        }
        set
        {
            this.invoiceItems = value;
        }

    }

最后是反序列化器:

    public void ReadXml(XmlReader reader)
    {
        Regex regexTaxName = new Regex(@"tax\d_name");
        Regex regexTaxType = new Regex(@"tax\d_type");
        Regex regexTaxPercent = new Regex(@"tax\d_percent");
        Regex regexTaxNumber = new Regex(@"\d");
        List<Tax> taxesList = new List<Tax>();
        while (reader.Read())
        {
            Debug.WriteLine(reader.GetAttribute("name"));
            if (reader.NodeType == XmlNodeType.Element)
            {

                if (reader.Name.Equals("name", StringComparison.Ordinal))
                {
                    reader.Read();
                    this.Name = reader.Value;
                }
                else if (reader.Name.Equals("type", StringComparison.Ordinal))
                {
                    ProductType value = ProductType.Product;
                    reader.Read();
                    if (Enums.TryParse<ProductType>(reader.Value, out value))
                    {

                        this.Type = value;
                    }
                }
                else if (reader.Name.Equals("description", StringComparison.Ordinal))
                {
                    reader.Read();
                    this.Description = reader.Value;
                }
                else if (reader.Name.Equals("unit_cost", StringComparison.Ordinal))
                {
                    float value = 0;
                    reader.Read();
                    if (float.TryParse(reader.Value, out value))
                    {

                        this.UnitCost = value;
                    }
                }
                else if (reader.Name.Equals("quantity", StringComparison.Ordinal))
                {
                    int value = 0;
                    reader.Read();
                    if (int.TryParse(reader.Value, out value))
                    {

                        this.Quantity = value;
                    }
                }
                else if (reader.Name.Equals("discount", StringComparison.Ordinal))
                {
                    float value = 0;
                    reader.Read();
                    if (float.TryParse(reader.Value, out value))
                    {

                        this.Discount = value;
                    }
                }
                else if (reader.Name.Equals("discount_type", StringComparison.Ordinal))
                {
                    NumericalSignificance value = NumericalSignificance.Percent;
                    reader.Read();
                    if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
                    {

                        this.DiscountType = value;
                    }
                }
                else if (regexTaxName.IsMatch(reader.Name))
                {
                    int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);

                    if (taxesList.Count < taxNumber)
                    {
                        reader.Read();
                        Tax newTax = new Tax();
                        newTax.Name = reader.Value;
                        taxesList.Add(newTax);
                    }
                    else
                    {
                        reader.Read();
                        taxesList[taxNumber-1].Name = reader.Value;
                    }
                }
                else if (regexTaxPercent.IsMatch(reader.Name))
                {
                    int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
                    if (taxesList.Count > taxNumber)
                    {
                        Tax newTax = new Tax();
                        float value = 0;
                        reader.Read();
                        if (float.TryParse(reader.Value, out value))
                        {

                            newTax.TaxPercent = value;
                        }

                        taxesList.Add(newTax);
                    }
                    else
                    {
                        float value = 0;
                        reader.Read();
                        if (float.TryParse(reader.Value, out value))
                        {

                            taxesList[taxNumber-1].TaxPercent = value;
                        }
                    }
                }
                else if (regexTaxType.IsMatch(reader.Name))
                {
                    int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
                    if (taxesList.Count > taxNumber)
                    {
                        Tax newTax = new Tax();
                        NumericalSignificance value = NumericalSignificance.Percent;
                        reader.Read();
                        if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
                        {

                            newTax.Type = value;
                        }

                        taxesList.Add(newTax);
                    }
                    else
                    {
                        NumericalSignificance value = NumericalSignificance.Percent;
                        reader.Read();
                        if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
                        {

                            taxesList[taxNumber-1].Type = value;
                        }
                    }
                }
            }
        }
        taxesArr = taxesList.ToArray();
    }

问题出在 items 数组上,其中最终对象只有最终对象,而不是所有原始对象。

编辑:

说明问题的示例:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;


[XmlRoot("invoice")]
public class Invoice
{
    [XmlArray("items")]
    private InvoiceExampleItem[] items;

    [XmlArray("items")]
    public InvoiceExampleItem[] Items
    {
        get { return this.items; }
        set { this.items = value; }
    }
}

[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem : IXmlSerializable
{
    [XmlElement(ElementName = "name")]
    public string Name { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {

                if (reader.Name.Equals("name", StringComparison.Ordinal))
                {
                    reader.Read();
                    this.Name = reader.Value;
                }
            }
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteElementString("name", this.Name);
    }
}




    [TestClass]
    public class ExampleTest : AutoMoqTest
    {
        [TestMethod]
        public void ExampleDeserialization()
        {
            InvoiceExampleItem item1 = new InvoiceExampleItem()
            {
                Name = "item1"
            };

            InvoiceExampleItem item2 = new InvoiceExampleItem()
            {
                Name = "item2"
            };

            InvoiceExampleItem item3 = new InvoiceExampleItem()
            {
                Name = "item3"
            };


            Invoice mockInvoice = new Invoice()
            {
                Items = new InvoiceExampleItem[] { item1, item2, item3 }
            };
            XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);

            XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);

            GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);

            Invoice resultInvoice = response.Invoice;

            if (mockInvoice.Items.Length != resultInvoice.Items.Length)
            {
                throw new Exception("wrong number of items");
            }

        }

        public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
        {
            XmlDocument toReturn = new XmlDocument();
            XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
            XmlReaderSettings settings = new XmlReaderSettings();

            XmlDocument itemsDocument = GetTemplateXML();
            InvoiceExampleItem[] items = invoiceToSerialize.Items;
            MemoryStream memStm = null, tempStream = null;
            try
            {
                memStm = tempStream = new MemoryStream();

                using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
                {
                    // Serialize object into raw xml
                    memStm = null;
                    serializer.Serialize(sw, invoiceToSerialize);
                    sw.Flush();

                    // parse raw xml into Xml document
                    tempStream.Position = 0;
                    settings.IgnoreWhitespace = true;
                    var xtr = XmlReader.Create(tempStream, settings);

                    toReturn.Load(xtr);
                }
            }
            finally
            {
                if (memStm != null)
                {
                    memStm.Dispose();
                }
            }

            return toReturn;
        }

        public static T DeserializeXML<T>(string responseString)
            where T : class
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            using (StringReader sr = new StringReader(responseString))
            {
                return (T)xmlSerializer.Deserialize(sr);
            }
        }

        public XmlDocument GetTemplateXML()
        {
            XmlDocument toReturn = new XmlDocument();
            var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
            toReturn.AppendChild(decl);

            return toReturn;
        }

        private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
        {
            XmlDocument toReturn = GetTemplateXML();

            XmlElement requestElement = toReturn.CreateElement("response");
            requestElement.SetAttribute("status", "success");
            requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
            toReturn.AppendChild(requestElement);

            return toReturn;
        }

        /// <summary>
        /// Web response from creating a invoice
        /// </summary>
        [System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
        [System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
        public class GetInvoiceResponse
        {
            /// <summary>
            /// Gets or sets the response status
            /// </summary>
            /// <value>
            /// reponse Status
            /// </value>
            [XmlAttribute("status")]
            public string ResponseStatus { get; set; }

            /// <summary>
            /// Gets or sets the new invoice id
            /// </summary>
            /// <value>
            /// generated by invoicera for this response
            /// </value>
            [XmlElement(ElementName = "invoice")]
            public Invoice Invoice { get; set; }
        }
    }

解决方案是为数组创建一个 class 并在其中实现 IXMLSeriazable 接口 class 并从项目 class 中删除该接口。

然后,当项目 class 中的 xml 时,我循环标记并单独创建一个项目,然后将其添加到数组中。

由于某些原因,该方法在处理每个 <\item> 标记时没有退出,所以我添加了一个退出循环的条件。

完整代码如下:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

[XmlRoot("invoice")]
public class Invoice
{
    public Items items { get; set; }

}

[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem
{
    [XmlElement(ElementName = "name")]
    public string Name { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {

                if (reader.Name.Equals("name", StringComparison.Ordinal))
                {
                    reader.Read();
                    this.Name = reader.Value;
                    return;
                }
            }
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteElementString("name", this.Name);
    }
}


public class Items : IXmlSerializable
{
    [XmlElement("item")]
    public List<InvoiceExampleItem> list = new List<InvoiceExampleItem>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        while (reader.ReadToFollowing("item"))
        {
            InvoiceExampleItem currentItem = new InvoiceExampleItem();
            currentItem.Name = reader.Value;
            list.Add(currentItem);
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        foreach(InvoiceExampleItem item in list)
        {
            writer.WriteStartElement("item");
            item.WriteXml(writer);
            writer.WriteEndElement();

        }
    }
}


    [TestClass]
    public class ExampleTest : AutoMoqTest
    {
        [TestMethod]
        public void ExampleDeserialization()
        {
            /**/
            InvoiceExampleItem item1 = new InvoiceExampleItem()
            {
                Name = "item1"
            };

            InvoiceExampleItem item2 = new InvoiceExampleItem()
            {
                Name = "item2"
            };

            InvoiceExampleItem item3 = new InvoiceExampleItem()
            {
                Name = "item3"
            };

        Items items = new Items();
        InvoiceExampleItem item21 = new InvoiceExampleItem()
        {
            Name = "item1"
        };

        InvoiceExampleItem item22 = new InvoiceExampleItem()
        {
            Name = "item2"
        };

        InvoiceExampleItem item23 = new InvoiceExampleItem()
        {
            Name = "item3"
        };
        items.list.Add(item21);
        items.list.Add(item22);
        items.list.Add(item23);

        Invoice mockInvoice = new Invoice()
            {
                items = items
        };
            /**/
            XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);

            XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);

        GetInvoiceResponse test = new GetInvoiceResponse();
            GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);

            Invoice resultInvoice = response.Invoice;
        mockResponseXml.Save("C:\Users\360Imprimir\Desktop\mockResponseXml");
        mockInvoiceSerialized.Save("C:\Users\360Imprimir\Desktop\mockInvoiceSerialized.Xml");
        if (mockInvoice.items.list.Count != resultInvoice.items.list.Count)
            {
                throw new Exception("wrong number of items");
            }

        }

        public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
        {
            XmlDocument toReturn = new XmlDocument();
            XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
            XmlReaderSettings settings = new XmlReaderSettings();

            XmlDocument itemsDocument = GetTemplateXML();

            MemoryStream memStm = null, tempStream = null;
            try
            {
                memStm = tempStream = new MemoryStream();

                using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
                {
                    // Serialize object into raw xml
                    memStm = null;
                    serializer.Serialize(sw, invoiceToSerialize);
                    sw.Flush();

                    // parse raw xml into Xml document
                    tempStream.Position = 0;
                    settings.IgnoreWhitespace = true;
                    var xtr = XmlReader.Create(tempStream, settings);

                    toReturn.Load(xtr);
                }
            }
            finally
            {
                if (memStm != null)
                {
                    memStm.Dispose();
                }
            }

            return toReturn;
        }

        public static T DeserializeXML<T>(string responseString)
            where T : class
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            using (StringReader sr = new StringReader(responseString))
            {
                return (T)xmlSerializer.Deserialize(sr);
            }
        }

        public XmlDocument GetTemplateXML()
        {
            XmlDocument toReturn = new XmlDocument();
            var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
            toReturn.AppendChild(decl);

            return toReturn;
        }

        private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
        {
            XmlDocument toReturn = GetTemplateXML();

            XmlElement requestElement = toReturn.CreateElement("response");
            requestElement.SetAttribute("status", "success");
            requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
            toReturn.AppendChild(requestElement);

            return toReturn;
        }

        /// <summary>
        /// Web response from creating a invoice
        /// </summary>
        [System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
        [System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
        public class GetInvoiceResponse
        {
            /// <summary>
            /// Gets or sets the response status
            /// </summary>
            /// <value>
            /// reponse Status
            /// </value>
            [XmlAttribute("status")]
            public string ResponseStatus { get; set; }

            /// <summary>
            /// Gets or sets the new invoice id
            /// </summary>
            /// <value>
            /// generated by invoicera for this response
            /// </value>
            [XmlElement(ElementName = "invoice")]
            public Invoice Invoice { get; set; }
        }
    }