Объект, полученный в результате десериализации xml, имеет только 1 элемент, несмотря на то, что xml имеет несколько элементов - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь проверить сериализацию для веб-запросов.Я делаю модульное тестирование, где я: - создаю ложный ответ с сервера - десериализирую этот ответ - сравниваю исходный объект с десериализованным

Проблема заключается в том, что один из моих массивов заполнен лишь частично, где вместо всехэлементы, у него есть только один, последний.Десериализация должна быть сделана вручную из-за ограничений схемы XML.Item является частичным классом для отделения 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();
    }

Проблемы в массиве элементов, где конечный объект имеет только конечный объект вместо всехоригинальные.

РЕДАКТИРОВАТЬ:

Пример, показывающий проблему:

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

1 Ответ

0 голосов
/ 13 февраля 2019

Решением было создание класса для массива и реализация интерфейса IXMLSeriazable в этом классе и удаление интерфейса из класса элементов.

Затем, когда 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; }
        }
    }
...