I have the following code, which I use to traverse the XML:
private void btn_readXML_Click(object sender, EventArgs e)
{
var doc = new XmlDocument();
doc.Load("e:\\contacts.xml");
// Load xml document.
TraverseNodes(doc.ChildNodes);
}
static List<string> xmlnodes = new List<string>();
private static void TraverseNodes(XmlNodeList nodes)
{
foreach (XmlNode node in nodes)
{
List<string> temp = new List<string>();
temp.Add("Node name: " + node.Name.ToString());
XmlAttributeCollection xmlAttributes = node.Attributes;
foreach (XmlAttribute at in xmlAttributes)
{
temp.Add(" Atrib: " + at.Name + ": " + at.Value);
}
xmlnodes.AddRange(temp);
TraverseNodes(node.ChildNodes);
}
But my problem is, I don't want to traverse the whole document, I only want to traverse the node and subsequently its children which has an attribute 'X'. Please note that I don't know where the node is present. So basically what I have to do is, find out if the node exists ( it'll have the attribute 'X'. That's how I identify its the right node) if yes then fetch its children.
Can anyone help me out here? I'm pretty new to XMLs. Thanks is advance!
Assuming your XML having following structure:
<Contacts>
<Contact X="abc">
<Child1></Child1>
</Contact>
<Contact X="def">
<Child2></Child2>
</Contact>
</Contacts>
Example code using XmlNode.SelectNodes:
var doc = new XmlDocument();
doc.Load("e:\\contacts.xml");
//get root element of document
XmlElement root = doc.DocumentElement;
//select all contact element having attribute X
XmlNodeList nodeList = root.SelectNodes("//Contact[#X]");
//loop through the nodelist
foreach (XmlNode xNode in nodeList)
{
//traverse all childs of the node
}
For different XPath Queries see this link.
UPDATE:
If you want to select all elements having attribute X in the document. No matters where they exists. You could use following:
//select all elements in the doucment having attribute X
XmlNodeList nodeList = root.SelectNodes("//*[#X]");
Try this:
private void btn_readXML_Click(object sender, EventArgs e)
{
var doc = new XmlDocument();
doc.Load("e:\\contacts.xml");
var nodes = xdoc.SelectNodes("//yournodename");
// ex.
// var nodes = xdoc.SelectNodes("//Company");
TraverseNodes(nodes);
}
Related
I want to read particular node in XML like if any "Log"(root node) node contain "Message" node the it should read all the node under the "Log" node.
Note : Log node is root node and there are many node under "log" node.
for Example :
<TestLogDataSet>
<Log>
<Assembly>TestCase</Assembly>
<TestMethod>Application</TestMethod>
<Status>Passed</Status>
<Trace />
</Log>
<Log>
<Assembly>TestCase</Assembly>
<TestMethod>Application</TestMethod>
<Status>Failed</Status>
<Message>
<pre><![CDATA[ Error while deleting the Project]]>
</pre>
</Message>
<Trace />
</Log>
</TestLogDataSet>
Code :
string xmlFile = File.ReadAllText(#"D:\demo.xml");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlFile);
foreach (XmlNode lognode in xmlDoc.SelectNodes("/TestLogDataSet/Log[Message]"))
{
foreach (XmlNode node in lognode.ChildNodes)
{
string n1 = node.InnerText;
textBox1.Text = n1 + "\r\n";
}
}
You can use XPath for this.
StringBuilder nodeText = new StringBuilder();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(<your xml here>);
foreach (XmlNode lognode in xmlDoc.SelectNodes("/TestLogDataSet/Log[Message]")) //select all log nodes with Message as child tag
{
string status = lognode.SelectSingleNode("./Status").InnerText;
if (!string.Equals(status,"failed",StringComparison.OrdinalIgnoreCase))
{
continue;
}
foreach (XmlNode node in lognode.ChildNodes)
{
nodeText.Append(node.LocalName);
nodeText.Append(":");
nodeText.Append(node.InnerText);//read inner text of node here
nodeText.Append("\n");
}
}
Console.WriteLine(nodeText.ToString());
If you want the Log nodes then this should suffice:
var nodes =
xd
.Root
.Elements("Log")
.Where(x => x.Element("Message") != null);
That gives:
If you want a list of all of the child nodes (which is what I understand you want from your question, but it seems a bit odd) then this works:
var nodes =
xd
.Root
.Elements("Log")
.Where(x => x.Element("Message") != null)
.SelectMany(x => x.Elements());
This gives:
I have an xml file as below
<Games>
<Game>
<name>Tzoker</name>
<file>tzoker1</file>
</Game>
<Game>
<file>lotto770</file>
</Game>
<Game>
<name>Proto</name>
<file>proto220</file>
</Game>
</Games>
I want to get the values of name and file items for every Game node.
It is easy by using this query.
string query = String.Format("//Games/Game");
XmlNodeList elements1 = xml.SelectNodes(query);
foreach (XmlNode xn in elements1)
{
s1 = xn["name"].InnerText;
s2 = xn["file"].InnerText;
}
The problem is that there are some nodes that they don't have the name item. So the code above doesn't work.
I have solved the problem by using the following code
string query = String.Format("//Games/Game/name");
XmlNodeList elements1 = xml.SelectNodes(query);
foreach (XmlNode xn in elements1)
{
s1 = xn.InnerText;
string query1 = String.Format("//Games/Game[name='{0}']/file", s1);
XmlNodeList elements2 = xml.SelectNodes(query1);
foreach (XmlNode xn2 in elements2)
{
s2 = xn2.InnerText;
}
}
The problem is that there is a case that two or more nodes have the same name value. So, the s2 variable will get the file value of the last node that the loop finds. So, I would like to find a way to get the sibling file value of the current name item. How could I do it? I try do move to the parent node of the current node and then to move to the file item but without success by using the following code.
string query = String.Format("//Games/Game/name");
XmlNodeList elements1 = xml.SelectNodes(query);
foreach (XmlNode xn in elements1)
{
s1 = xn.InnerText;
string query1 = String.Format("../file");
XmlNodeList elements2 = xml.SelectNodes(query1);
foreach (XmlNode xn2 in elements2)
{
s2 = xn2.InnerText;
}
}
I hope there is a solution.
You can use Game[name] to filter Game elements to those with child element name. This is possible because child:: is the default axes which will be implied when no explicit axes mentioned. Extending this further to check for child element file as well, would be as simple as Game[name and file] :
string query = String.Format("//Games/Game[name]");
XmlNodeList elements1 = xml.SelectNodes(query);
foreach (XmlNode xn in elements1)
{
s1 = xn["name"].InnerText;
s2 = xn["file"].InnerText;
}
Now to answer your question literally, you can use following-sibling:: axes to get sibling element that follows current context element. So, given the context element is name, you can do following-sibling::file to return the sibling file element.
Your attempt which uses ../file should also work. The only problem was, that your code executes that XPath on xml, the XmlDocument, instead of executing it on current name element :
XmlNodeList elements2 = xn.SelectNodes("../file");
If I understand you correctly you want to find all games that have a name. You can do that using XPath. Here is a solution that uses LINQ to XML. I find that easier to work with than XmlDocument:
var xDocument = XDocument.Parse(xml);
var games = xDocument
.Root
.XPathSelectElements("Game[child::name]")
.Select(
gameElement => new {
Name = gameElement.Element("name").Value,
File = gameElement.Element("file").Value
}
);
The XPath to select all <Game> elements that have a <name> child element is Game[child::name].
I would like to get XmlNodeList from a huge XML file.
Conditions:
I have a List of unique ID values, say IDList
Case I: Collect all the nodes where element called ID has value from IDList.
Case II: Collect all nodes where one of the attribute called idName of element ID has value from IDList.
In short, extract only the nodes which match with the values given in the IDList.
I did this using some loops like load this XML to XmlDocument to iterate over all nodes and ID value but what I am looking for is some sophisticated method to do it faster and in quick way.
Because looping isn't a solution for a large XML file.
My try:
try
{
using (XmlReader reader = XmlReader.Create(URL))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList nodeList = doc.GetElementsByTagName("idgroup");
foreach (XmlNode xn in nodeList)
{
string id = xn.Attributes["id"].Value;
string value = string.Empty;
if (IDList.Contains(id))
{
value = xn.ChildNodes[1].ChildNodes[1].InnerText; // <value>
if (!string.IsNullOrEmpty(value))
{
listValueCollection.Add(value);
}
}
}
}
}
catch
{}
XML (XLIFF) structure:
<XLIFF>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">
<file date="2013-07-17">
<body>
<id idName="test_001" >
<desc-group name="test_001">
<desc type="text"/>
</desc-group>
<result-unit idName="test_001_text">
<source>abcd</source>
<result>xyz</result>
</result-unit>
</id>
</body>
</file>
</xliff>
Collect all the nodes like above where idName matches.
EDIT
This is a test that can parse the example you are giving. It attempts to reach the result node directly, so that it stays as efficient as possible.
[Test]
public void TestXPathExpression()
{
var idList = new List<string> { "test_001" };
var resultsList = new List<string>();
// Replace with appropriate method to open your URL.
using (var reader = new XmlTextReader(File.OpenRead("fixtures\\XLIFF_sample_01.xlf")))
{
var doc = new XmlDocument();
doc.Load(reader);
var root = doc.DocumentElement;
// This is necessary, since your example is namespaced.
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("x", "urn:oasis:names:tc:xliff:document:1.2");
// Go directly to the node from which you want the result to come from.
foreach (var nodes in idList
.Select(id => root.SelectNodes("//x:file/x:body/x:id[#idName='" + id + "']/x:result-unit/x:result", nsmgr))
.Where(nodes => nodes != null && nodes.Count > 0))
resultsList.AddRange(nodes.Cast<XmlNode>().Select(node => node.InnerText));
}
// Print the resulting list.
resultsList.ForEach(Console.WriteLine);
}
You can extract only those nodes you need by using an XPath query. A brief example on how you 'd go about it:
using (XmlReader reader = XmlReader.Create(URL))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
foreach(var id in IDList) {
var nodes = doc.SelectNodes("//xliff/file/body/id[#idName='" + id + "']");
foreach(var node in nodes.Where(x => !string.IsNullOrEmpty(x.ChildNodes[1].ChildNodes[1].InnerText)))
listValueCollection.Add(node.ChildNodes[1].ChildNodes[1].InnerText);
}
}
The xpath expression is of course an example. If you want, you can post an example of your XML so I can give you something more accurate.
I need to take in XML with common parent nodes but with varying child nodes. Once I get it, I need to grab the tag names of the child nodes and use those names as headers. In the following example, all incoming XML will be wrapped as follows:
<customers>
<customer>
...varying child nodes that do not have child nodes themselves
</customer>
</customers>
I have found that this works:
List<string> headerList = new List<string>();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(someXML);
XmlNodeList xnl = xmlDoc.SelectNodes("customers/customer");
foreach (XmlNode xn in xnl)
{
for (int x = 0; x < xn.ChildNodes.Count; x++)
{
headerList.Add(xn.ChildNodes[x].Name.ToString());
}
}
Is there a better way to do this?
Thanks in advance.
This should do the trick;
XDocument doc = XDocument.Load(someXML);
var headerList = doc.Descendants("customer").Elements().Select(x => x.Name);
Not necessarily "better", but it's a bit more concise I guess.
XML file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<content>
<FingerPrintUserDetails>
<UserID>e57645</UserID>
<UserName>Jill</UserName>
<FPData>AQAAABQAAAD0AAAAAQASAAEAWgAAAAAA8AAAAHfrWpB6BHZBL10voxpdxu2Km5XVNh*oUNC80Rvpql3RhlqOiBPeet6iRPwb/BdN1fCF4Y/WHbQ40*mqoUKqilgN7bUqNuXP7M299HUWtoAGEO3nDKXXAnHd7dgytZbmHVv*mRBPJDSRw9VY/R1yOIu2cCDlLM*F8Q1lvTfMFDdfwNZynI0e2ZauCF58f0UX56XLFBallaAauxP5mvvhUmcmc6ITg7RhH9wc4181kgPjCuZg38pQepE5U07XIa3hQP8fwxPzdprifXECgB1Z3pTXWQP0q4ZD0Inlbq6Gszo1ucPrhQA0jYQRXtJUVuyBeg</FPData>
<Address>Pune</Address>
<ContactNo>848488484884</ContactNo>
</FingerPrintUserDetails>
<FingerPrintUserDetails>
<UserID>444</UserID>
<UserName>John</UserName>
<FPData>AQAAABQAAADkAAAAAQASAAEAZAAAAAAA4AAAAPLnmQ8FymAAHWYutR5pdtYvfDVmjsqLeli8tOSTFAtw6AkfA0r8XwrMzp9jFZJI7DlBk4G94BMq55gPEG7uBLZUNYrvhv0jDlDFMOjWGJ9RoWekFveTC*oZ7Tq/xmxuvY6FzLHVo*xzdKQI73Y0f9/eeMC0OgqnbQ3I0IP6cTkkKnTUZJOXKr7IFPHkjJAvCDmU7ec4vG50JHdBJIObmbzVcO0huTUQyE7CR1qYkUjmNFKgVKWPLRupEk4l/Ek0BuAba*9JlhBVUHzZuKbDQLc9lTFwevAgDuuAwxfZaLS*</FPData>
<Address>nagpur</Address>
<ContactNo>464645763</ContactNo>
</FingerPrintUserDetails>
<FingerPrintUserDetails>
<UserID>5555</UserID>
<UserName>Jack</UserName>
<FPData>AQAAABQAAAAEAQAAAQASAAEAZAAAAAAA9AAAAPz5mQO3uTeXLfU5Mb74XbCX5rERGZFPQMVG1vPpX87306O5oURlYiIe5dasJ2S8NlBZu2UU3zaUpNnB7viYDB6*wfFlgtopn/WdbXW0Yhik3hj8nDreEmaK12To8qfAJx2ooq43i0wBIL*0Jkba*QpHIprSajrhnCg1PjOLMP37sEauJUwXJaoDR/PPQYIxTFE5kf8xzGlJmqiGejD*Y8R3ewU9yIrxkdQ0S//LCdacULt2QvS/I3APo/j0FAgSCOU3SBLdDL6UBPD4fLeEzo7uUIW8gUMThzZX*u2iUuNwJdqWC2NsFtWkUWt03sz3xYQpR8pLA4vrsUmldzUMWe8</FPData>
<Address>beed</Address>
<ContactNo>5745745747</ContactNo>
</FingerPrintUserDetails>
</content>
C#:
XmlDocument doc = new XmlDocument();
doc.Load("E://BioEnable_Project//fp_project_using_xml//fp_project_using_xml//Capture_Data.xml");
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("FPData");
foreach(XmlElement node in nodes)
{
MessageBox.Show(node.Value);
}
I have to check FPData value on each node..i use above code but not getting..
In your XPath, provide the full path to the node.
XmlNodeList nodes = root.SelectNodes("/content/FingerPrintUserDetails/FPData");
What is happening is that there is no direct FPData node under the document root.
XmlNodeList nodes = root.SelectNodes("content/FingerPrintUserDetails");
it will return array of FingerPrintUserDetails, then find FPData in them
XmlNodeList res = nodes[index].SelectNodes("FPData");
Using LINQ to XML:
XDocument doc = XDocument.Load("XmlFilePath");
var selectors = from elements in doc.Elements("content").Elements("FingerPrintUserDetails")
select elements;
foreach (var element in selectors)
{
MessageBox.Show(element.Element("FPData").Value);
}
XmlDocument doc = new XmlDocument();
doc.Load("E://BioEnable_Project//fp_project_using_xml//fp_project_using_xml//Capture_Data.xml");
XmlNodeList lst = doc.GetElementsByTagName("FingerPrintUserDetails");
foreach (XmlElement elem in lst)
{
XmlNode pfData = doc.GetElementsByTagName("FPData")[0];
MessageBox.Show(pfData.Value);
}