DeSerialize From XML By LINQ - c#

So, I have XML file:
<?xml version="1.0" encoding="utf-8"?>
<RailwayStations>
<RailwayStation />
<RailwayStationName>Verdansk</RailwayStationName>
<RailwayStationCountOfWays>10</RailwayStationCountOfWays>
<RailwayStationCountOfLuggageRooms>3</RailwayStationCountOfLuggageRooms>
<RailwayStationLuggageRoomHeight>10</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>20</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>30</RailwayStationLuggageRoomDepth>
<RailwayStationLuggageRoomHeight>11</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>21</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>31</RailwayStationLuggageRoomDepth>
<RailwayStationLuggageRoomHeight>12</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>22</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>32</RailwayStationLuggageRoomDepth>
</RailwayStations>
And, I want to read from it. My code below returns null to all fields
var xDoc = XDocument.Load(fileName);
var obj = from xElement in xDoc.Element("RailwayStations")?.Elements("RailwayStation")
select new RailwayStation()
{
RailwayStationName = xElement.Element("RailwayStationName")?.Value,
RailwayStationCountOfWays = Convert.ToInt32(xElement.Element("RailwayStationCountOfWays")?.Value),
RailwayStationCountOfLuggageRooms =
Convert.ToInt32(xElement.Element("RailwayStationCountOfLuggageRooms")?.Value),
LuggageRooms = (from element in xDoc.Element("RailwayStations")?.Elements("RailwayStation")
select new LuggageRoom()
{
_luggageRoomHeight = Convert.ToInt32(element.Element("RailwayStationLuggageRoomHeight")?.Value),
_luggageRoomWidth = Convert.ToInt32(element.Element("RailwayStationLuggageRoomHeight")?.Value),
_luggageRoomDepth = Convert.ToInt32(element.Element("RailwayStationLuggageRoomHeight")?.Value),
}).ToList()
};
return obj;
Any suggestions? About XML File - it created by self-made method, where I add XElements to XDocument and save it.

Based on the expectation in your code, it looks like your XML is not well-formed, this is what your code is expecting:
<?xml version="1.0" encoding="utf-8"?>
<RailwayStations>
<RailwayStation>
<RailwayStationName>Verdansk</RailwayStationName>
<RailwayStationCountOfWays>10</RailwayStationCountOfWays>
<RailwayStationCountOfLuggageRooms>3</RailwayStationCountOfLuggageRooms>
<LuggageRooms>
<LuggageRoom>
<RailwayStationLuggageRoomHeight>10</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>20</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>30</RailwayStationLuggageRoomDepth>
</LuggageRoom>
<LuggageRoom>
<RailwayStationLuggageRoomHeight>11</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>21</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>31</RailwayStationLuggageRoomDepth>
</LuggageRoom>
<LuggageRoom>
<RailwayStationLuggageRoomHeight>12</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>22</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>32</RailwayStationLuggageRoomDepth>
</LuggageRoom>
</LuggageRooms>
</RailwayStation>
<RailwayStation>
<RailwayStationName>Number 2</RailwayStationName>
<RailwayStationCountOfWays>8</RailwayStationCountOfWays>
<RailwayStationCountOfLuggageRooms>1</RailwayStationCountOfLuggageRooms>
<LuggageRooms>
<LuggageRoom>
<RailwayStationLuggageRoomHeight>12</RailwayStationLuggageRoomHeight>
<RailwayStationLuggageRoomWidth>22</RailwayStationLuggageRoomWidth>
<RailwayStationLuggageRoomDepth>32</RailwayStationLuggageRoomDepth>
</LuggageRoom>
</LuggageRooms>
</RailwayStation>
</RailwayStations>
Notice now that RailwayStations (plural) now has multiple child elements called RailwayStation. The same then goes for the Luggage rooms, the code is actually making the wrong assumption for these anyway, but the data should be structured so that each luggage room is contained within an outer element, in this example I called it LuggageRooms
var xDoc = XDocument.Load(fileName);
var obj = from xElement in xDoc.Element("RailwayStations")?.Elements("RailwayStation")
select new RailwayStation()
{
RailwayStationName = xElement.Element("RailwayStationName")?.Value,
RailwayStationCountOfWays = Convert.ToInt32(xElement.Element("RailwayStationCountOfWays")?.Value),
RailwayStationCountOfLuggageRooms =
Convert.ToInt32(xElement.Element("RailwayStationCountOfLuggageRooms")?.Value),
LuggageRooms = (from element in xElement.Elements("LuggageRooms")
select new LuggageRoom()
{
_luggageRoomHeight = Convert.ToInt32(element.Element("RailwayStationLuggageRoomHeight")?.Value),
_luggageRoomWidth = Convert.ToInt32(element.Element("RailwayStationLuggageRoomWidth")?.Value),
_luggageRoomDepth = Convert.ToInt32(element.Element("RailwayStationLuggageRoomDepth")?.Value),
}).ToList()
};
return obj;
It looks like you have an XY problem here, if you are constructing the XML as well, please check the logic in there to make sure that it makes sense.
If you are constructing this XML, then consider a schema with simpler named elements:
<RailwayStation>
<Name>Number 2</Name>
<CountOfWays>8</CountOfWays>
<CountOfLuggageRooms>1</CountOfLuggageRooms>
<LuggageRooms>
<LuggageRoom>
<Height>12</Height>
<Width>22</Width>
<Depth>32</Depth>
</LuggageRoom>
</LuggageRooms>
</RailwayStation>
Then your code could be something like this:
var xDoc = XDocument.Load(fileName);
var obj = from xElement in xDoc.Element("RailwayStations")?.Elements("RailwayStation")
select new RailwayStation()
{
Name = xElement.Element("Name")?.Value,
CountOfWays = Convert.ToInt32(xElement.Element("CountOfWays")?.Value),
CountOfLuggageRooms = Convert.ToInt32(xElement.Element("CountOfLuggageRooms")?.Value),
LuggageRooms = (from element in xElement.Elements("LuggageRooms")
select new LuggageRoom()
{
Height = Convert.ToInt32(element.Element("Height")?.Value),
Width = Convert.ToInt32(element.Element("Width")?.Value),
Depth = Convert.ToInt32(element.Element("Depth")?.Value),
}).ToList()
};
return obj;
I realize this is a significant structure change, but it will simplify all future processing and reduce the bytes going across the wire.

Related

how to print the innertext of an element based on attribute search of a particular node using LINQ?

**I have an XML like this-
<?xml version="1.0" encoding="UTF-8"?>
<Tool_Parent>
<tool name="ABCD" id="226">
<category>Centralized</category>
<extension_id>0</extension_id>
<uses_ids>16824943 16824944</uses_ids>
</tool>
<tool name="EFGH" id="228">
<category>Automated</category>
<extension_id>0</extension_id>
<uses_ids>92440 16824</uses_ids>
</tool>
</Tool_Parent>
Based on the id of tool i want to print the uses_ids value,i.e if i search for 228 i should get 92440 16824.
I had tried like-
var toolData = (from toolElement in doc.Descendants("tool")
select new Tool_poco
{
a_Name = tool.Attribute("name").Value,
a_Id = tool.Attribute("id").Value,
e_ExtensionId = tool.Element("extension_id").Value,
e_UsesIds =tool.Element("uses_parm_ids").Value
});
where Tool_poco is a poco class for tool node containing declaration for member variable.
Now I want to get information related to a particular tool id in toolData variable.How to do it?
Note: I have variable like-
searched_Tool_id = Tool_Id_txtBx.Text.ToString();
Please let me know a way through which i can modify my above query for toolData.**
You can modify your query as
Tool_poco toolData = (from el in xelement.Elements("Employee")
where (string)el.Attribute("id") == "226"
select new Tool_poco
{
a_Name = el.Attribute("name").Value,
a_Id = el.Attribute("id").Value,
e_ExtensionId = el.Element("Name").Value,
e_UsesIds = el.Element("uses_ids").Value
}).FirstOrDefault();
You could start by doing something like this once you have an XDocument object loaded and ready:
var xdoc = XDocument.Parse(
#"<?xml version=""1.0"" encoding=""utf-8""?>
<Tool_Parent>
<tool name=""ABCD"" id=""226"">
<category>Centralized</category>
<extension_id>0</extension_id>
<uses_ids>16824943 16824944</uses_ids>
</tool>
<tool name=""EFGH"" id=""228"">
<category>Automated</category>
<extension_id>0</extension_id>
<uses_ids>92440 16824</uses_ids>
</tool>
</Tool_Parent>");
var root = xdoc.Root; // Got to have that root
if (root != null)
{
var id228query = (from toolElement in root.Elements("tool")
where toolElement.HasAttributes
where toolElement.Attribute("id").Value.Equals("228")
let xElement = toolElement.Element("uses_ids")
where xElement != null
select xElement.Value).FirstOrDefault();
Console.WriteLine(id228query);
Console.Read();
}
Output: 92440 16824
**Note: In reference to your example, one possible reason it was not working
for you could be that your xml references an element with name "uses_ids",
however, your query references an element with a similar, but not exact,
spelling with name "uses_parm_ids".**

Best way to index, search and store information in C#

I'm writing an application which will need to index and store information about files fast. I'm currently using XML to store the information using this code:
XmlTextWriter xtw;
xtw = new XmlTextWriter(FilePath, Encoding.UTF8);
xtw.WriteStartDocument();
xtw.WriteStartElement("ApplicationIndex");
xtw.WriteEndElement();
xtw.Close();
XmlDocument xd = new XmlDocument();
FileStream lfile = new FileStream(FilePath, FileMode.Open);
xd.Load(lfile);
XmlElement cl = xd.CreateElement("Application");
cl.SetAttribute("Name", ApplicationName);
XmlElement na = xd.CreateElement("Path");
XmlText natext = xd.CreateTextNode(ApplicationPath);
na.AppendChild(natext);
cl.AppendChild(na);
XmlElement na1 = xd.CreateElement("UseCount");
XmlText natext1 = xd.CreateTextNode("0");
na1.AppendChild(natext1);
cl.AppendChild(na1);
XmlElement na2 = xd.CreateElement("SearchTerm");
XmlText natext2 = xd.CreateTextNode(ApplicationName.ToLower());
na2.AppendChild(natext2);
cl.AppendChild(na2);
xd.DocumentElement.AppendChild(cl);
lfile.Close();
xd.Save(FilePath);
This works fine for creating the file and storing the data, however I'm having trouble searching through the data quickly as there are several hundred nodes in the document. I've tried using Linq to XML to achieve this using this code:
listBox1.Items.Clear();
var doc = XDocument.Load(filePath);
foreach (var child in doc.Descendants("SearchTerm"))
{
if (child.Value.Contains(textBox1.Text.ToLower()))
{
listBox1.Items.Add(child.Value);
}
}
This is very fast however I can't seem to get any information about the selected node. For example I would like to sort the returned results based upon the UseCount (The higher the count the higher up the list). Is there anyway to do this in XML or any other technique to achieve this?
This is what the XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationIndex>
<Application Name="Google Chrome">
<Path>C:\Program Files\Google\Chrome\Chrome.exe</Path>
<UseCount>0</UseCount>
<SearchTerm>google chrome</SearchTerm>
</Application>
<Application Name="Mozilla Firefox">
<Path>C:\Program Files\Mozilla\Firefox\Firefox.exe</Path>
<UseCount>0</UseCount>
<SearchTerm>mozilla firefox</SearchTerm>
</Application>
</ApplicationIndex>
You can Sort your elements by UseCount in descending order like this:
var doc = XDocument.Load(filePath);
var elements = doc.Descendants("Application")
.OrderByDescending(x => (int)x.Element("UseCount"));
In order to search a record by given SearchTerm you can do the following:
var element = doc.Descendants("Application")
.FirstOrDefault(x => (string)x.Element("SearchTerm") == value);
if(element != null)
{
// record found
}

simple way to read xml data using linq

I have a xml structure like this. Can anyone help with a simple linq function to read this xml structure.The itemEntry node repeats according to data. I tried to read the xml using the method below,but i am getting no records in the list. Is this method here correct way to get the details...
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements("itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element("title"),
YEAR = (string)e.Element("year"),
ITEMNAME = (string)e.Element("itemname"),
CATRYLIST =
(
from p in e.Elements("categorylist").Elements("categories")
select new CATLIST
{
IDTYPE = (string)p.Element("categoryid"),
IDNUMBER = (string)p.Element("categoryName")
}).ToList()
}).ToList();
<itemslist>
<itemInformation>
<itemdate>01/23/2014</itemdate>
<itemcount>57</itemcount>
</itemInformation>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
</itemslist>
You should try with XDocument.
XDocument xdoc = XDocument.Load("file.xml");
The System.Xml.XLinq namespace contains some awesome objects to make this easy.
var xDoc = XDocument.Parse(xml); // load your xml string, or use XDocument.Load() to load an xml file
var itemEntries = xDoc
.Root // refers to itemEntries node
.Descendants("itemEntry"); // gets all itemEntry nodes in an IEnumerable object
This gets you an IEnumerable<XNode> of all the itemEntry nodes.
From there you can do what you need, save the values to a business object, etc.
The above method works properly, i found the issue, my xml tag was having namespace attribute. i tried to get the namespace and append it with Elements while reading
XNamespace ns = xDocument.Root.Attribute("xmlns").Value;
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements(ns + "itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element(ns + "title"),
YEAR = (string)e.Element(ns + "year"),
ITEMNAME = (string)e.Element(ns + "itemname"),
CATRYLIST =
(
from p in e.Elements(ns + "categorylist").Elements(ns + "categories")
select new CATLIST
{
IDTYPE = (string)p.Element(ns + "categoryid"),
IDNUMBER = (string)p.Element(ns + "categoryName")
}).ToList()
}).ToList();

Renaming Child Nodes in XML file using C#?

I am having a problem renaming child nodes in xml files using c#.
This is my xml file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<ZACAC01>
<IDOC BEGIN="1">
<ZACGPIAD SEGMENT="1">
<IDENTIFIER>D000</IDENTIFIER>
<CUST_DEL_NO/>
<CUST_DEL_DATE/>
<TRUCKNO/>
<DRIVERNAME/>
<DRIVERID/>
<RESPONS_OFF/>
<CONFIRM_DATE>20/01/13</CONFIRM_DATE>
<SERIAL_NO>2</SERIAL_NO>
<SERIAL_CHAR/>
<DEL_INFO1/>
<QTY>0</QTY>
<DEL_INFO2/>
<QTY>0</QTY>
<DEL_INFO3/>
<QTY>0</QTY>
<TRANS_COMPANY>0</TRANS_COMPANY>
</ZACGPIAD>
</IDOC>
</ZACAC01>
And below is my requirement:
<?xml version="1.0" encoding="ISO-8859-1"?>
<ZACAC01>
<IDOC BEGIN="1">
<ZACGPIADD SEGMENT="1">
<IDENTIFIER>D000</IDENTIFIER>
<CUST_DEL_NO/>
<CUST_DEL_DATE/>
<TRUCKNO/>
<DRIVERNAME/>
<DRIVERID/>
<RESPONS_OFF/>
<CONFIRM_DATE>20/01/13</CONFIRM_DATE>
<SERIAL_NO>2</SERIAL_NO>
<SERIAL_CHAR/>
<DEL_INFO1/>
<QTY1>0</QTY1>
<DEL_INFO2/>
<QTY2>0</QTY2>
<DEL_INFO3/>
<QTY3>0</QTY3>
<TRANS_COMPANY>0</TRANS_COMPANY>
</ZACGPIADD>
</IDOC>
</ZACAC01>
I am able to change the segment tag <ZACGPIAD> to this <ZACGPIADD> using the following code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(srcfile);
var root = xmlDoc.GetElementsByTagName("IDOC")[0];
var oldElem = root.SelectSingleNode("ZACGPIAD");
var newElem = xmlDoc.CreateElement("ZACGPIADD");
root.ReplaceChild(newElem, oldElem);
while (oldElem.ChildNodes.Count != 0)
{
newElem.AppendChild(oldElem.ChildNodes[0]);
}
while (oldElem.Attributes.Count != 0)
{
newElem.Attributes.Append(oldElem.Attributes[0]);
}
xmlDoc.Save(desfile);
But I can't change the <QTY> tag to <QTY1>, <QTY2>, <QTY3>
How can I do this?
I think you have the answer right in your code. You can use SelectSingleNode to pull the first <QTY> element with this:
var qtyNode = root.SelectSingleNode("ZACAC01/IDOC/ZACGPIADD/QTY[1]")
then use ReplaceChild on it's parent node. Then do the same for the second and third <QTY> nodes, replacing the '1' with '2' and '3' respectively.
You can use XDocument and operate on XElements which expose setter for node name (so you can simply set new name instead of doing node replacements):
var doc = XDocument.Load(srcfile);
var zacgpidNode = doc.Descendants("ZACGPIAD").First();
zacgpidNode.Name = "ZACGPIADD";
// now rename all QTY nodes
var qtyNodes = zacgpidNode.Elements("QTY").ToArray();
for (int i = 0; i < qtyNodes.Length; i++)
{
qtyNodes[i].Name = string.Format("{0}{1}", qtyNodes[i].Name, i+1);
}
doc.Save(desfile);
Having Descendants("ZACGPIAD").First() might not be suitable if your document structure is different than what you've shown in example. You can use XPathSelectElement method to have more control over what you'll be extracting:
var node = doc.XPathSelectElement("//IDOC[#BEGIN='1']/ZACGPIAD[#SEGMENT='1']");

Querying XML elements with identical name with Linq

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<ProductTypes>
<ProductType Name="MyProduct">
<Amount>100</Amount>
<Pattern Length="1" AllowedCharacters="ABCD"/>
<Pattern Length="7" AllowedCharacters="EFGH"/>
</ProductType>
</ProductTypes>
Using Linq to XML I can successfully extract information from any number of the element ProductType. However I also need the information from all of the elements Pattern.
XElement xml = XElement.Load("pattern_config.xml");
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value)
// How to get all Pattern elements from that ProductType?
};
How can I do this? Or would you recommend another way accessing this XML?
You can nest queries.
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value),
// How to get all Pattern elements from that ProductType?
Patterns = from patt in productType.Elements("Pattern")
select new { Length = int.Parse(patt.Attribute("Length").Value),
.... }
};

Categories

Resources