I am trying to read the following file, I can read the attributes, but I can't go into the specific element (Address in this case) and read its elements based on the attribute of that (Address) element. Shortly I need to distinguish between work and home addresses. I need to do this with XMLReader class. Can you help?
<Address Label="Work">
<Name>Name1</Name>
<Street>PO 1</Street>
<City>City1</City>
<State>State 1</State>
</Address>
<Address Label="Home">
<Name>Name2</Name>
<Street>PO 2</Street>
<City>City2</City>
<State>State 2</State>
</Address>"
Okay, here are some notes to think about. XMLReader in the sense i understand you use it (with no code example) is that you iterate over the document, since the XMLReader is forward-only, and read-only.
Because of this you need to iterate until you find the node you need. In the example below i find the address element labeled "work" and extract that entire node. Then query on this node as you want.
using (var inFile = new FileStream(path, FileMode.Open))
{
using (var reader = new XmlTextReader(inFile))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name == "Address" && reader.GetAttribute(0) == "Work")
{
// Create a document, which will contain the address element as the root
var doc = new XmlDocument();
// Create a reader, which only will read the substree <Address> ... until ... </Address>
doc.Load(reader.ReadSubtree());
// Use XPath to query the nodes, here the "Name" node
var name = doc.SelectSingleNode("//Address/Name");
// Print node name and the inner text of the node
Console.WriteLine("Node: {0}, Inner text: {1}", name.Name, name.InnerText);
}
break;
}
}
}
}
Edit
Made an example that not uses LINQ
XML:
<Countries>
<Country name ="ANDORRA">
<state>Andorra (general)</state>
<state>Andorra</state>
</Country>
<Country name ="United Arab Emirates">
<state>Abu Z¸aby</state>
<state>Umm al Qaywayn</state>
</Country>
Java:
public void datass(string file)
{
string file = HttpContext.Current.Server.MapPath("~/App_Data/CS.xml");
XmlDocument doc = new XmlDocument();
if (System.IO.File.Exists(file))
{
//Load the XML File
doc.Load(file);
}
//Get the root element
XmlElement root = doc.DocumentElement;
XmlNodeList subroot = root.SelectNodes("Country");
for (int i = 0; i < subroot.Count; i++)
{
XmlNode elem = subroot.Item(i);
string attrVal = elem.Attributes["name"].Value;
Response.Write(attrVal);
XmlNodeList sub = elem.SelectNodes("state");
for (int j = 0; j < sub.Count; j++)
{
XmlNode elem1 = sub.Item(j);
Response.Write(elem1.InnerText);
}
}
}
Using XPath you can easily write concise expressions to navigate an XML document.
You would do something like
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(myXMLString);
XmlNode homeAddress = xDoc.SelectSingleNode("//Address[#Label='Work']");
Then do whatever you want with homeAddress.
Read more here on w3schools on XPath.
Related
I found this question:
How to remove an xml element from file?
Which seems to work fine if you know some info within the element you want to delete.
But I have a OnItemDeleting function in ASP.NET where I only have (I think) the Selected Index of the item in a ListView.
In my C# file I have defined two alternatives (A and B) as you can see, it looks like this:
System.Diagnostics.Debug.WriteLine("IN ON ITEM DELETING.");
ListView1.SelectedIndex = e.ItemIndex;
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(path);
XmlNodeList nodes = xmldoc.GetElementsByTagName("EmployeeInformation");
for (int i = 0; i < nodes.Count; i++)
{
if (i == ListView1.SelectedIndex)
{
nodes[i].RemoveChild(nodes[i]); // Alt. A
xmldoc.RemoveChild(nodes[i]); // Alt. B
break;
}
}
xmldoc.Save(path);
BindDatalist();
If I try something like A, I dont know how to replace the nodes in the XmlDocument with the nodes in the XmlNodeList, and if I do like B it just doesn't work and also its weird.
The XML file looks like this:
<EmployeeInformation>
<Details>
<Name>Goofy</Name>
<Emp_id>Goooof</Emp_id>
<Qualification>BBA</Qualification>
</Details>
<Details>
<Name>Donald</Name>
<Emp_id>Duck</Emp_id>
<Qualification>MTech</Qualification>
</Details>
<Details>
<Name>Donald</Name>
<Emp_id>Trump</Emp_id>
<Qualification>MCA</Qualification>
</Details>
</EmployeeInformation>
So lets say I want to remove the Donald Trump item by clicking a button next to it. The selectedIndex would be 2.
In your case looping XmlNodeList not required.
try this
XmlDocument doc = new XmlDocument();
doc.Load(path);
if (ListView1.SelectedIndex < doc.DocumentElement.ChildNodes.Count)
{
doc.DocumentElement.RemoveChild(doc.DocumentElement.ChildNodes[ListView1.SelectedIndex]);
doc.Save(path);
}
Specifying that the node to be deleted from the XlmNodeList is a parent node solved the problem:
ListView1.SelectedIndex = e.ItemIndex;
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(path);
XmlNodeList nodes = xmldoc.GetElementsByTagName("Details");
for (int i = 0; i < nodes.Count; i++)
{
if (i == e.ItemIndex)
{
nodes[i].ParentNode.RemoveChild(nodes[i]);
break;
}
}
xmldoc.Save(path);
BindDatalist();
I have the xml below in a c# class.
string xml =#"<?xml version='1.0' encoding='UTF-8'?>
<call method='importCube'>
<credentials login='sampleuser#company.com' password='my_pwd' instanceCode='INSTANCE1'/>
<importDataOptions version='Plan' allowParallel='false' moveBPtr='false'/>
I need to update the XML held within the node attributes i.e.
string xml =#"<?xml version='1.0' encoding='UTF-8'?>
<call method='importCube'>
<credentials login='testuser#test.com' password='userpassword' instanceCode='userinstance'/>
<importDataOptions version='Actual' allowParallel='true' moveBPtr='true'/>
I've written code to do this :
// instantiate XmlDocument and load XML from string
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
// get a list of nodes
XmlNodeList aNodes = doc.SelectNodes("/call/credentials");
// loop through all nodes
foreach (XmlNode aNode in aNodes)
{
// grab the attribute
XmlAttribute idLogin = aNode.Attributes["login"];
XmlAttribute idPass = aNode.Attributes["password"];
XmlAttribute idInstance = aNode.Attributes["instanceCode"];
idLogin.Value = "myemail.com";
idPass.Value = "passtest";
idInstance.Value = "TestInstance";
}
It works but the issue at the minute is that I have to repeat the whole code block for each node path i.e.
XmlNodeList aNodes = doc.SelectNodes("/call/importDataOptions");
....
There has to be a better way. Any ideas how I can rip through the attributes in 1 pass?
Maybe just using a cast to XmlElement could help you to reduce your code:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
foreach (XmlElement credential in doc.SelectNodes("/call/credentials"))
{
credential.SetAttribute("login" , "myemail.com" );
credential.SetAttribute("password" , "passtest" );
credential.SetAttribute("instanceCode", "TestInstance");
}
Another option is to create an object structure which resembles your XML vocabulary and deserialize your input into that objects, but it seems overkill.
EDIT: Per your comment, you could go with:
foreach (XmlElement node in doc.SelectNodes("/call/*")) // it's case sensitive
{
switch(node.Name)
{
case "credentials":
node.SetAttribute("login" , "myemail.com" );
node.SetAttribute("password" , "passtest" );
node.SetAttribute("instanceCode", "TestInstance");
break;
case "importDataOptions":
// ...
break;
default:
throw new ArgumentOutOfRangeException("Unexpected node: "+node.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.
The following is the XML file read into XmlDocument
<Test xmlns="http://api.test.com/v2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Result id="2015" description="Invalid Token" />
</Test >
What I need is the 'id' attribute value ("2015") stored in some TextBox
This is how XmlDocument is loaded
XmlDocument updateUser = new XmlDocument();
updateUser.Load(response.GetResponseStream());
Works well till here.
Then, create namespace and search for node
XmlNamespaceManager nsmgr = new XmlNamespaceManager(updateUser.NameTable);
nsmgr.AddNamespace("restup", "http://api.test.com/v2");
XmlNodeList locationElements1 = updateUser.SelectNodes("//restup:Test", nsmgr);
foreach (XmlNode Test in locationElements1)
{
//What DO I do here to get the value of 'id' attribute from the 'Result' node and save it in txtTest Textbox.
}
var id = Test.FirstChild.Attributes["id"].Value;
string idString = Test.FirstChild.Attributes["id"].ToString();
hello this is another method that can be useful
XmlTextReader reader = new XmlTextReader(fileLocation); //fileLocation is the Path of the XML file
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element) //if the node is an element (not a comment, CDATA, or text)
if (reader.Name == "Result")
textBox1.Text = reader.GetAttribute("id");
}
reader.Close();
I'd like to get all the element name from a xml file, for example the xml file is,
<BookStore>
<BookStoreInfo>
<Address />
<Tel />
<Fax />
<BookStoreInfo>
<Book>
<BookName />
<ISBN />
<PublishDate />
</Book>
<Book>
....
</Book>
</BookStore>
I would like to get the element's name of "BookName". "ISBN" and "PublishDate " and only those names, not include " BookStoreInfo" and its child node's name
I tried several ways, but doesn't work, how can I do it?
Well, with XDocument and LINQ-to-XML:
foreach(var name in doc.Root.DescendantNodes().OfType<XElement>()
.Select(x => x.Name).Distinct())
{
Console.WriteLine(name);
}
There are lots of similar routes, though.
Using XPath
XmlDocument xdoc = new XmlDocument();
xdoc.Load(something);
XmlNodeList list = xdoc.SelectNodes("//BookStore");
gives you a list with all nodes in the document named BookStore
I agree with Adam, the ideal condition is to have a schema that defines the content of xml document. However, sometimes this is not possible. Here is a simple method for iterating all of the nodes of an xml document and using a dictionary to store the unique local names. I like to keep track of the depth of each local name, so I use a list of int to store the depth. Note that the XmlReader is "easy on the memory" since it does not load the entire document as the XmlDocument does. In some instances it makes little difference because the size of the xml data is small. In the following example, an 18.5MB file is read with an XmlReader. Using an XmlDocument to load this data would have been less effecient than using an XmlReader to read and sample its contents.
string documentPath = #"C:\Docs\cim_schema_2.18.1-Final-XMLAll\all_classes.xml";
Dictionary<string, List<int>> nodeTable = new Dictionary<string, List<int>>();
using (XmlReader reader = XmlReader.Create(documentPath))
{
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
if (!nodeTable.ContainsKey(reader.LocalName))
{
nodeTable.Add(reader.LocalName, new List<int>(new int[] { reader.Depth }));
}
else if (!nodeTable[reader.LocalName].Contains(reader.Depth))
{
nodeTable[reader.LocalName].Add(reader.Depth);
}
}
reader.Read();
}
}
Console.WriteLine("The node table has {0} items.",nodeTable.Count);
foreach (KeyValuePair<string, List<int>> kv in nodeTable)
{
Console.WriteLine("{0} [{1}]",kv.Key, kv.Value.Count);
for (int i = 0; i < kv.Value.Count; i++)
{
if (i < kv.Value.Count-1)
{
Console.Write("{0}, ", kv.Value[i]);
}
else
{
Console.WriteLine(kv.Value[i]);
}
}
}
The purists way of doing this (and, to be fair, the right way) would be to have a schema contract definition and read it in that way. That being said, you could do something like this...
List<string> nodeNames = new List<string>();
foreach(System.Xml.XmlNode node in doc.SelectNodes("BookStore/Book"))
{
foreach(System.Xml.XmlNode child in node.Children)
{
if(!nodeNames.Contains(child.Name)) nodeNames.Add(child.Name);
}
}
This is, admittedly, a rudimentary method for obtaining the list of distinct node names for the Book node's children, but you didn't specify much else in the way of your environment (if you have 3.5, you could use LINQ to XML to make this a little prettier, for example), but this should get the job done regardless of your environment.
If you're using C# 3.0, you can do the following:
var data = XElement.Load("c:/test.xml"); // change this to reflect location of your xml file
var allElementNames =
(from e in in data.Descendants()
select e.Name).Distinct();
You can try doing it using XPATH.
XmlDocument doc = new XmlDocument();
doc.LoadXml("xml string");
XmlNodeList list = doc.SelectNodes("//BookStore/Book");
If BookStore is ur root element then u can try following code
XmlDocument doc = new XmlDocument();
doc.Load(configPath);
XmlNodeList list = doc.DocumentElement.GetElementsByTagName("Book");
if (list.Count != 0)
{
for (int i = 0; i < list[0].ChildNodes.Count; i++)
{
XmlNode child = list[0].ChildNodes[i];
}
}