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:
- xmlSerializer
- 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.