How to Read XML node attributes? - c#

Hi all below is my XML file.
<print>
<part keyName="logo" fontName="" fontSize="" fontColor="" bold="" italic="" underline="" maxWidth="45" textAlign="center" isBarcode="" isImage="true">
<logo>testimg.jpg</logo>
</part>
<part keyName="header" fontName="" fontSize="" fontColor="" bold="" italic="" underline="" maxWidth="45" textAlign="center" isBarcode="" isImage="">
<SpaceSep>0</SpaceSep>
<LineSep>1</LineSep>
<text fontSize="20">Tax Invoice</text>
<LineSep>1</LineSep>
<text>Test Pvt Ltd</text>
<LineSep>1</LineSep>
<address/>
<area/>
<city/>
<state/>
<pin/>
<country/>
<LineSep>1</LineSep>
<text>Phone: </text>
<phone></phone>
<LineSep>1</LineSep>
<text>GSTIN: </text>
<gstIn></gstIn>
<LineSep>1</LineSep>
</part>
</print>
Above XML file contains parent root as Print, And child nodes as part.
I want to read child nodes and it's attributes in C#. If XML file contains unique node names then I can read them. But if all child's contains same node name then how can we read them.

If i'm understanding well your question, you should do something like that:
//...
using System.Linq;
using System.Xml.Linq;
//...
XDocument doc = XDocument.Load(#"C:\directory\file.xml");
IEnumerable<XElement> partElements = doc.Root.Elements("part");
foreach (XElement partElement in partElements)
{
// read attribute value
string keyName = partElement.Attribute("keyName")?.Value;
//...
// iterate through childnodes
foreach (XElement partChildElement in partElement.Elements())
{
// check the name
if (partChildElement.Name == "SpaceSep")
{
int value = (int)partChildElement; // casting from element to its [int] content
// do stuff for <SpaceSep> element
}
else if (partChildElement.Name == "text")
{
string text = (string)partChildElement; // casting from element to its [string] content
// do stuff for <text> element
}
// and so on for all possible node name
}
}

Related

Select a subset of childnodes by name

Given this xml doc
<listOfItem>
<Item id="1">
<attribute1 type="foo"/>
<attribute2 type="bar"/>
<property type="x"/>
<property type="y"/>
<attribute3 type="z"/>
</Item>
<Item>
//... same child nodes
</Item>
//.... other Items
</listOfItems>
Given this xml document, I would like to select, for each "Item" node, just the "property" child nodes. How can I do it in c# directly? With "directly" I mean without selecting all the child nodes of Item and then check one by one. So far:
XmlNodeList nodes = xmldoc.GetElementsByTagName("Item");
foreach(XmlNode node in nodes)
{
doSomething()
foreach(XmlNode child in node.ChildNodes)
{
if(child.Name == "property")
{
doSomethingElse()
}
}
}
You can use SelectNodes(xpath) method instead of ChildNodes property:
foreach(XmlNode child in node.SelectNodes("property"))
{
doSomethingElse()
}
Demo.
Try using LINQ to XML instead of XML DOM as it's much simpler syntax for what you want to do.
XDocument doc = XDocument.Load(filename);
foreach (var itemElement in doc.Element("listOfItems").Elements("Item"))
{
var properties = itemElement.Elements("property").ToList();
}

Formatting an elements text using descendants of XElement

Im wondering how to select a specific element from the heirarchy so that I can format its text.
In the example below I would like to format the specific element to remove the time portion of the date, but I am also looking for a way to format any of the elements for example to add a currency symbol to the text between each price tag.
My example
<orders>
<order>
<type> tools </type> //I would like the ability to select this element
<text> screwdriver </text>
<id> 100981 </id>
<price> 5.00 </price>
<date> 01/01/15 12:51:36 </date>
</order>
<order>
<type> uniform </type>
<text> boots </text>
<id> 100546 </id>
<price> 25.00 </price>
<date> 12/01/15 15:30:41 </date>
</order>
</orders>
What I have so far
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );// set the first letter of each output to be uppercase
}
}
What I have tried
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
if( element.Descendants() == element.Element("date"))
{
element.Value = Convert.ToDateTime(element.Value).ToShortDateString();
}
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );
}
}
I have some XML experience but have never worked with XElement before.
I've been searching SO for a while now but cant find what Im looking for. The answers below are some of the suggested answers from typing this question but they dont provide a solution as the XML elements are generated dynamically in a loop.
XElement node with text node has its formatting ignored
string.Format in XElement not formatting
Any help with this would be great as I have not attempted this before. Thanks.
You can get the text node's parent using Parent property and check it's name:
foreach (XText node in doc.DescendantNodes()
.Where(x => NodeType == XmlNodeType.Text))
{
if(node.Parent.Name == "date") { ... }
if(node.Parent.Name == "price") { ... }
}
BTW, don't forget the save the document using XDocument.Save method after you made the changes.
I usually create one class to represent the xml and send yo the instance of object with correct type. for Example convert.ToDecimal(node.innerText)

Select Parent XML(Entire Hierarchy) Elements based on Child element values LINQ

I have the following XML and query through the ID,how do get the Parent Hierarchy
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>
In this if i query(Id = 4) and find out the Parent elements using Linq in the particular element how to get the following output with Hierarchy.
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4"/>
</Child3>
</Child2>
</Child1>
</Child>
Thanks In Advance.
Assume you want just one node parent tree:
string xml = #"<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
TextReader tr = new StringReader(xml);
XDocument doc = XDocument.Load(tr);
IEnumerable<XElement> myList =
from el in doc.Descendants()
where (string)el.Attribute("Id") == "4" // here whatever you want
select el;
// select your hero element in some way
XElement hero = myList.FirstOrDefault();
foreach (XElement ancestor in hero.Ancestors())
{
Console.WriteLine(ancestor.Name); // rebuild your tree in a separate document, I print ;)
}
To search for every element of your tree iterate retrieve the node with the select query without the where clause and call the foreach for every element.
Based on the sample XML provided, you could walk up the tree to find the parent node once you've found the node in question:
string xml =
#"<Child>
<Child1 Id='1'>
<Child2 Id='2'>
<Child3 Id='3'>
<Child4 Id='4'>
<Child5 Id='5'/>
<Child6 Id='6'/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
var doc = XDocument.Parse( xml );
// assumes there will always be an Id attribute for each node
// and there will be an Id with a value of 4
// otherwise an exception will be thrown.
XElement el = doc.Root.Descendants().First( x => x.Attribute( "Id" ).Value == "4" );
// discared all child nodes
el.RemoveNodes();
// walk up the tree to find the parent; when the
// parent is null, then the current node is the
// top most parent.
while( true )
{
if( el.Parent == null )
{
break;
}
el = el.Parent;
}
In Linq to XML there is a method called AncestorsAndSelf on XElement that
Returns a collection of elements that contain this element, and the
ancestors of this element.
But it will not transform your XML tree the way you want it.
What you want is:
For a given element, find the parent
Remove all elements from parent but the given element
Remove all elements from the given element
Something like this in Linq (no error handling):
XDocument doc = XDocument.Parse("<xml content>");
//finding element having 4 as ID for example
XElement el = doc.Descendants().First(el => el.Attribute("Id").Value == "4");
el.RemoveNodes();
XElement parent = el.Parent;
parent.RemoveNodes();
parent.Add(el);
[Edit]
doc.ToString() must give you what you want as a string.
[Edit]
Using RemoveNodes instead of RemoveAll, the last one also removes attributes.
Removing nodes from the chosen element too.
I found the following way
XElement elementNode = element.Descendants()
.FirstOrDefault(id => id.Attribute("id").Value == "4");
elementNode.RemoveNodes();
while (elementNode.Parent != null)
{
XElement lastNode = new XElement(elementNode);
elementNode = elementNode.Parent;
elementNode.RemoveNodes();
elementNode.DescendantsAndSelf().Last().AddFirst(lastNode);
}
return or Print elementNode.

Add an XML node to multiple parent nodes(which have same name)

I am trying to add an XML node to multiple parent nodes(which have same name). But it is only adding to the Last node of the XML and not in all.
input XML
<Record>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
</Record>
I want to add the Location element to every Emp node. My code is as below:
XmlNodeList xNodeList = doc.SelectNodes("/Record/Emp");
XmlElement xNewChild = doc.CreateElement("Location");
xNewChild.InnerText = "USA";
foreach (XmlNode item in xNodeList)
{
item.AppendChild(xNewChild);
}
doc.Save(path);
but I am getting output like this:
<Record>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
<Location>USA</Location>
</Emp>
</Record>
The Location element has not been added to the first Emp node.
Note: After debugging, I am able to find that the element has been added even for the first Emp node. But, in the saved XML file I am seeing this strange behavior.
Your xNewChild is a single new element. Simply adding it to multiple nodes will only serialize to the last node. A change like this should work:
XmlNodeList xNodeList = doc.SelectNodes("/Record/Emp");
foreach (XmlNode item in xNodeList)
{
XmlElement xNewChild = doc.CreateElement("Location");
xNewChild.InnerText = "USA";
item.AppendChild(xNewChild);
}
doc.Save(path);

search xml elements of a file based on another xml file which acts as a search criteria

I am trying to search or get the xml elements of one file based on a query xml file. This query xml file defines which elements will be searched and retrieve their value. The code below does not find all the elements in the xml file even though the element is there:
Anyone can tell me how can I improve my code, or figure out what is the problem?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace ReadXMLRecursively
{
class Program
{
static void Main(string[] args)
{
doc.Load(#"C:\Requests.xml");
doc2.Load(#"C:XmlLists.xml");
XmlNodeList nList2 = doc2.SelectNodes("//Root/Element");
XmlNodeList nList = doc.SelectNodes("//Root/List");
foreach (XmlNode xmlNode in nList)
{
if (nList2 != null)
foreach (XmlNode n in nList2)
{
if (n.Attributes != null)
{
string val = n.Attributes.GetNamedItem("SourceCol").Value;
if (xmlNode[val] != null)
{
//if (n.Attributes != null) Console.WriteLine(n.Attributes.GetNamedItem("SourceCol").Value);
XmlElement xmlElement = xmlNode[val];
if (xmlElement != null) Console.WriteLine(xmlElement.Name);
}
else
{
Console.WriteLine(val + " not found");
}
}
}
Console.WriteLine("------------- end------------------");
}
}
}
XML File 1
<root>
<list>
<FirstName>Abc</FirstName>
<LastName>LT</LastName>
<Occupatoin>Eng</Occupation>
<BirthDate></BirthDate>
...
</list>
</root>
XML File 2
<root>
<Trainings>
<Java>Ab</Java>
<NET>b</NET>
<SQL>c</SQL>
<Powershell>d</Powershell>
...
</Trainings>
Search the above xml files base on this xml file
<root>
<Element Name="Firstname />
<Element Name="Lastname" />
<Element Name="Occupation" />
<Element Name="Java" />
<Element Name="Net" />
...
</root>
You wrote:
XmlNodeList nList = doc.SelectNodes("//Root/List");
In your example there is just one element with the name "list", and so you will get in nList just one node, with one long string that contains all the children nodes, so you cann't get them by loop.
If you want to get the children of "list" any element in different node you need to change it to:
XmlNodeList nList = doc.SelectNodes("//Root/List/*");

Categories

Resources