Home > Enterprise >  How to serialize XML to object from particular part of XML?
How to serialize XML to object from particular part of XML?

Time:10-26

I have problem how to serialize XML to list of objects but only from particular part of one, big XML. I mean something like that:

take data only from items and make list of item objects.

<tXML>
    <nameOfUser>MK</nameOfUser>
    <amouthOfPO>14</amouthOfPO>
    <todayDate></todayDate>
    <warehouses>
        <warehouse id="1">
            <name>AD1</name>
        </warehouse>
        <warehouse id="2">
            <name>AD2</name>
        </warehouse>
        <warehouse id="3">
            <name>AD3</name>
        </warehouse>
    </warehouses>
    <items>
        <warehause id="1">
            <items>
                <item>
                    <name>item1</name>
                    <protectionLevel>AMB</protectionLevel>
                    <description>DescAMB1FORID1</description>
                </item>
                <item>
                    <name>item2</name>
                    <protectionLevel>CHL</protectionLevel>
                    <description>DescCHL1FORID1</description>
                </item>
                <item>
                    <name>item3</name>
                    <protectionLevel>AMB</protectionLevel>
                    <description>3</description>
                </item>
            </items>
        </warehause>
        <warehause id="3">
            <items>
                <item>
                    <name>item1AMB2222</name>
                    <protectionLevel>AMBB222222</protectionLevel>
                    <description>DESCRIPTIONITEM1AM222222B</description>
                </item>
                <item>
                    <name>item222222CHL</name>
                    <protectionLevel>C222222222LL</protectionLevel>
                    <description>ITEM2CH22ILLERAD1</description>
                </item>
                <item>
                    <name>2222222222223</name>
                    <protectionLevel>222222223</protectionLevel>
                    <description>3222222222222</description>
                </item>
            </items>
        </warehause>
        <warehause id="3">
            <items>
                <item>
                    <name>item1333333AMB</name>
                    <protectionLevel>AM3333BB</protectionLevel>
                    <description>DESCR333IPTIONITEM1AMB</description>
                </item>
                <item>
                    <name>item233333CHL</name>
                    <protectionLevel>C33333HLL</protectionLevel>
                    <description>ITEM2CHI333LLERAD1</description>
                </item>
                <item>
                    <name>33</name>
                    <protectionLevel>33</protectionLevel>
                    <description>33</description>
                </item>
            </items>
        </warehause>
    </items>
</tXML>

My item class looks like below :

namespace UseFiles
{
    public class Item
    {
        public string Name { get; set; }

        public string ProtectionLevel { get; set; }

        public string Description { get; set; }
    }
}

What is the best (optimally) way to change XML (only from ) for objects ? I thought about two ways:

  1. xmlSerializer
  2. or just to assign values in loop from XMLNodeList ..

CodePudding user response:

First of all best (optimally) way can be interpreted as: 1 - best performance, 2 - more (easy) readable code.

The best performance I suppose shoul be working with FileStream using XmlReader and XmlWriter. But in your case this can cause some additional complications, because items located inside wherehouses, and any item should be inside particular 'wherehouse'. So you should find items insude concrete wherehouse, and after some updates save that items inside same wherehouse. Any items can be: 1 - updated, 2 - added, 3 - removed, from wherehouse, so updating of existing xml-document becomes much more complicated.

Based on the above, I recommend using LinqToXml, it not so performance efficient, but fast enought in most cases, have any tools you need (searching and replacing list of elements inside concrete xml-tag) and much more readable.

This is how you can use it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace TestProgram
{
    class Program
    {
        private const string _PATH = @"path to xml-file";

        static void Main(string[] args)
        {
            const string myWherehouseId = "3";
            
            var document = XDocument.Load(_PATH);
            var itemsElement = document.Root.Element("items");

            List<Item> myItems = null;

            foreach (XElement wherehause in itemsElement.Elements())
            {
                if (wherehause.Attribute("id").Value == myWherehouseId)
                {
                    myItems = FromXml(wherehause.Element("items").Elements());

                    // Do some things
                    myItems[0].Name  = "1";
                    myItems[0].Description  = "2";
                    myItems.RemoveAt(1);
                    myItems.Add(
                        new Item
                        { 
                            Name = "Name212",
                            Description = "Sometext123213",
                            ProtectionLevel = "CHL"
                        });

                    XElement[] updatedElements = ToXml(myItems);

                    // replace old xml nodes with new ones
                    wherehause.Element("items").ReplaceNodes(updatedElements);
                    break;
                }
            }

            document.Save(_PATH);
        }

        public static List<Item> FromXml(IEnumerable<XElement> elements)
        {
            // you could use any custom mapper for conversion from XElement to Item
            return elements.Select(el => new Item
            {
                Name = el.Element("name").Value,
                ProtectionLevel = el.Element("protectionLevel").Value,
                Description = el.Element("description").Value                
            }).ToList();
        }

        public static XElement[] ToXml(List<Item> items)
        {
            // you could use any custom mapper for conversion from Item to XElement
            return items
                .Select(item => 
                    new XElement("item", 
                        new XElement("name", item.Name),
                        new XElement("protectionLevel", item.ProtectionLevel),
                        new XElement("description", item.Description)))
                .ToArray();
        }
    }

    public class Item
    {
        public string Name { get; set; }

        public string ProtectionLevel { get; set; }

        public string Description { get; set; }
    }
}

In my opinion best (optimally) way consists of a reasonable compromise between performance and code readability.

This is my best way ;)

CodePudding user response:

Well, XmlReader way to go for parsing large xml files. Alternatively you can try Cinchoo ETL - an open source library to parse such large file with few lines of code.

using (var r = ChoXmlReader<Item>.LoadText(xml)
       .WithXPath("//item")
      )
{
    foreach (var rec in r)
        rec.Print();
}

Sample fiddle: https://dotnetfiddle.net/otYq5j

Disclaimer: I'm author of this library.

  • Related