I'd like to retrieve information from an XML file, however the way it's formatted is pretty strange. Here it is...
<?xml version="1.0"?>
<Careers>
<CareerList>
<CareerName></CareerName>
<CareerDescription></CareerDescription>
</CareerList>
<CareerList>
<CareerName>Cook</CareerName>
<CareerDescription>Cooks food for people</CareerDescription>
</CareerList>
</Careers>
I'd like to get the 2nd value, which would be Cook and the description which is Cooks food for people, but instead I'm getting only the empty node. For example...
public string CareerDescription(string CareerFile)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(CareerFile);
string Description = xmlDoc.SelectSingleNode("Careers/CareerList/CareerDescription").InnerText;
return Description;
}
How would I select the second node instead of the first?
You can use an index in your XPath expression:
xmlDoc.SelectSingleNode("Careers/CareerList[2]/CareerDescription").InnerText
Personally I'd use LINQ to XML instead, mind you:
var doc = XDocument.Load(CareerFile);
return doc.Root
.Elements("CareerList")
.ElementAt(1) // 0-based
.Element("CareerDescription")
.Value;
Instead of SelectSingleNode you should use SelectNodes: it will return XmlNodeList nodeList. Then you should take the InnerText of the element from that node list with index [1];
public string CareerDescription(string CareerFile)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(CareerFile);
string Description = xmlDoc.SelectNodes("Careers/CareerList/CareerDescription")[1].InnerText;
return Description;
}
Refer to the documentation on this method on MSDN for more details: http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.selectnodes%28v=vs.71%29.aspx
Just a straight way of LINQ to XML routine (and because it is LINQ, I prefer this way much more than the "standard" usage of XmlDocument with the support of XPath):
return XDocument.Load(CareerFile)
.Descendants("CareerDescription").Skip(1).First().Value;
Related
I am trying the below sample to select a node by ignoring the case and the select single node retuns null.
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><CHILD1>c1</CHILD1><CHILD2>c2</CHILD2></root>");
var node = doc.SelectSingleNode("root");
string nodeXpath = string.Format("//*[translate(#key, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']","child1");
node = node.SelectSingleNode(nodeXpath);
string innertext = node.InnerText;
Can someone help.
#key in XPath means a reference to an attribute named key. There is no such attribute in your XML. If you meant to match by element name then you're supposed to use name() or local-name() instead :
...
string xpath = "//*[translate(name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']";
string nodeXpath = string.Format(xpath,"child1");
...
You can use LINQ to Xml in little bid more readable way
XDocument doc = XDocument.Parse("<root><CHILD1>c1</CHILD1><CHILD2>c2</CHILD2></root>");
var singleNode =
doc.Root
.Elements()
.FirstOrDefault(element => element.Name.ToString().ToLower().Equals("child1"));
But notice that XML support different nodes where name can be case sensitive(for example "Node" and "node") and "searching" elements in "ignore case" way can lead to problems in the future.
I was working through this today and I used your solution. I just wrapped it in a function and call it whenever I need to match on an element name that's under the root. Works like a charm. Thanks!
private string GetNodeXpathCaseInsensitive(string value)
{
string xpath = String.Format("//*[translate(name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']", value.ToLower());
return xpath;
}
I am trying to select values from elements using the XPathNodeIterator. But I am not able to get the expected value from my XML file.
My XML file
<?xml version="1.0" encoding="utf-8"?>
<ns0:Entity xmlns:ns0="http://schemas.mycompany.com/1.0">
<ns0:Data>
<Address_Id xmlns:ns0="http://schemas.mycompany.com/1.0">7</Address_Id>
<Customer_Id xmlns:ns0="http://schemas.mycompany.com/1.0">67</Customer_Id>
<CustomerName xmlns:ns0="http://schemas.mycompany.com/1.0">My Customer 1</CustomerName>
</ns0:Data>
</ns0:Entity>
My methods to get the values
I have created two methods which both doesn't return the values which I would like to have.
private static string GetValue_Attempt1(XPathDocument xPathDocument, string xpathExpression)
{
var xpathNavigator = xPathDocument.CreateNavigator();
var xpathNodeIterator = xpathNavigator.Select(xpathExpression);
xpathNodeIterator.MoveNext();
return xpathNodeIterator.Current.Value;
}
private static string GetValue_Attempt2(XPathDocument xPathDocument, string xpathExpression)
{
var xpathNavigator = xPathDocument.CreateNavigator();
var xpathNodeIterator = xpathNavigator.Select(xpathExpression);
xpathNodeIterator.MoveNext();
var nodesNavigator = xpathNodeIterator.Current;
var nodesText = nodesNavigator.SelectDescendants(XPathNodeType.Text, false);
nodesText.MoveNext();
return nodesText.Current.Value;
}
My XPath
Get Address_Id
XPath: //Data/Address_Id/text()
Attempt1 returns: 767My Customer 1. Why are all values concatenated ???
Attempt2 returns: 7. Looks OK
Get Customer_Id
XPath: //Data/Customer_Id/text()
Attempt1 returns: 767My Customer 1. Why are all values concatenated ???
Attempt2 returns: 7. Why do I get the Address_Id??
Questions
Probably I am using an incorrect XPath. But also I don't understand the results. In order to understand whats going on. I would like to know:
Is there a better way (method) to get the value of the elements?
What is the XPath string which I need to use?
Why does method GetValue_Attempt1 return a concatinated string?
Why does method GetValue_Attempt2 return the value of only the first element?
Your XPath returns no matches as you're not taking the namespaces into account.
Your 'Attempt 1' calls MoveNext() once, so Value returns all the concatenated text in Data, and in 'Attempt 2' you call MoveNext() twice which positions you inside the Address_Id element.
In addition, Select, even it the XPath was valid, doesn't actually move the cursor.
If you wanted to get the value, you'd do something like this - note I'm including the namespace in the XPath and using the result of SelectSingleNode:
var nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("ns0", "http://schemas.mycompany.com/1.0");
var value = navigator.SelectSingleNode("//ns0:Data/Address_Id", nsm).Value;
However, do you have any reason to be using XPath and XPathDocument here? Using the much more expressive and (more) statically typed LINQ to XML would be far preferable, I'd think:
XNamespace ns = "http://schemas.mycompany.com/1.0";
var doc = XDocument.Parse(xml);
var addressId = (int)doc.Descendants(ns + "Data")
.Elements("Address_Id")
.Single();
var customerId = (int)doc.Descendants(ns + "Data")
.Elements("Customer_Id")
.Single();
I have my XML as
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<recordsDirectory>F:/model_RCCMREC/</recordsDirectory>
<transferDirectory>F:/model_RCCMrecTransfered/</transferDirectory>
<logDirectory>F:/model_RCCMLOG/</logDirectory>
<connectionstring>Data Source=192.168.1.7;Initial Catalog=RCCMdb;User ID=genesys;Password=genesys</connectionstring>
<table>RCCMrec</table>
<DBdestination>
<val1>ANI</val1>
<val2>DNIS</val2>
<val3>Date</val3>
<val4>Time</val4>
<val5>ConnId</val5>
<val6>UUID</val6>
<val7>EmployeeId</val7>
<val8>AgentDN</val8>
</DBdestination>
</configuration>
I need the value of the recordsDirectory tag.
I tried this,
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("recordsDirectory").InnerText.ToString();
But got an error saying
Object reference not set to an instance of an object.
Yes, SelectSingleNode("recordsDirectory") will return null, because you're applying that XPath to the document itself - which doesn't have a recordsDirectory element at the top level, it has a configuration element. You want:
xmldoc.SelectSingleNode("configuration/recordsDirectory")
Or go via the root element:
xmldoc.DocumentElement.SelectSingleNode("recordsDirectory")
(Or you can fetch all descendant elements call recordsDirectory, etc. There are plenty of options here.)
Personally I'd suggest changing to use LINQ to XML if you can, as it's a simpler way of using XML, IMO. It's not too bad in the code you've given so far, but as you do more things with XmlDocument you'll run into it being a bit of a pain - relatively speaking, anyway.
You should also consider separating the "fetching the node" from getting the text, so you can validate that you've found the one you want:
XmlNode node = xmldoc.DocumentElement.SelectSingleNode("recordsDirectory");
if (node != null)
{
// Use it
}
else
{
// No such node. What do you want to do?
}
Try this one in your SelectSingleNode
XmlNode node = doc.SelectSingleNode("/configuration/recordsDirectory");
string s = node.InnerText.ToString();
Hi To read the recordsDirectory tag you need to do :
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("C:/Users/yachna/Desktop/RCCM_TOOL/configRCCM.xml");
string bvalue = xmldoc.SelectSingleNode("configuration/recordsDirectory").InnerText.ToString();
It will work perfactly
I have a XML file, that looks like this inside:
<Data>
<INFO>
..
...
<JOB_NAME value="filename.pdf"/>
<useless_info value="some_info"/>
<TIMESTAMP value="20120210075304"/>
<more_useless_info value="012345"/>
...
..
</INFO>
<INFO>
..
...
<JOB_NAME value="filename2.pdf"/>
<useless_info value="some_info"/>
<TIMESTAMP value="20120210073487"/>
<more_useless_info value="012345"/>
...
..
</INFO>
</Data>
What i want to do, is write specific info to strings, so that later on i can write these to a text file or new XML file.
I found this example here: http://www.csharp-examples.net/xml-nodes-by-name/
And i have this code a little bit working.
Not fully, because it it not getting the values.
my code looks like this:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(FileNameTextBox.Text);
XmlNodeList xnList = xmlDoc.SelectNodes("/Data/INFO");
foreach (XmlNode xn in xnList)
{
string jobName = xn["JOB_NAME"].InnerText;
string timeStamp = xn["TIMESTAMP"].InnerText;
MessageBox.Show(timeStamp + jobName); //for testing
}
I think this has to do with the fact that the info that i want to get, is not boxed in like <box>info</box>
What i cant find is how i could get the info in my caseout of the xml file now.
Could someone lent me a hand?
thank you!
You need to select the JOB_NAME and TIMESTAMP child nodes of each INFO node, then get their attributes, then get the Value of the "value" attribute.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(FileNameTextBox.Text);
XmlNodeList xnList = xmlDoc.SelectNodes("/Data/INFO");
foreach (XmlNode xn in xnList)
{
string jobName = xn.SelectSingleNode("JOB_NAME").Attributes["value"].Value;
string timeStamp = xn.SelectSingleNode("TIMESTAMP").Attributes["value"].Value;
MessageBox.Show(timeStamp + jobName); //for testing
}
Be careful with this though as you are likely to get a NullReferenceException if any of the INFO nodes don't contain both a JOB_NAME and TIMESTAMP Node, and also if either of those do not have an attribute "value".
To answer your comment below:
string vendorName = xn.SelectSingleNode("JOB_NAME").Attributes["vendor-name"].Value;
string mediaName = xn.SelectSingleNode("JOB_NAME").Attributes["media-name"].Value;
You're correct in that innerText won't work because in your example 'value' is an attribute.
string jobName = xn["JOB_NAME"].Attributes["value"].Value;
string timeStamp = xn["TIMESTAMP"].Attributes["value"].Value;
Corrected and tested. This method or the SelectSingleNode should work fine.
If you want to iterate over all INFO elements and read the contained JOB_NAME and TIMESTAMPs valueattributes then Tobsey's answer is correct and will give you the desired result.
If you intend to query specific values from your Xml document you should use XPath queries instead of iterating over a set of nodes:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(FileNameTextBox.Text);
string jobName = xmlDoc.SelectSingleNode("//JOB_NAME/#value").Value;
string timeStamp = xmlDoc.SelectSingleNode("//TIMESTAMP/#value").Value;
Update
If you have repeating INFO nodes but just are interested in the first one you could change your query to just look at the first one:
string jobName = xmlDoc.SelectSingleNode("//INFO[1]/JOB_NAME/#value").Value;
string timeStamp = xmlDoc.SelectSingleNode("//INFO[1]/TIMESTAMP/#value").Value;
The Xpath query //INFO[1]/JOB_NAME/#value is a shorthand expression for //INFO[position() = 1]/JOB_NAME/#value and will examine the position of the node in your Xml document.
Beware! The simplified example above will fail if .SelectSingleNode returns null!
You should get the Value property of the attribute named "value" in your nodes:
string jobName = xn.SelectSingleNode("JOB_NAME").Attributes["value"].Value;
I have several XML files that I wish to read attributes from. My main objective is to apply syntax highlighting to rich text box.
For example in one of my XML docs I have: <Keyword name="using">[..] All the files have the same element: Keyword.
So, how can I get the value for the attribute name and put them in a collection of strings for each XML file.
I am using Visual C# 2008.
The other answers will do the job - but the syntax highlighting thingy and the several xml files you say you have makes me thinks you need something faster, why not use a lean and mean XmlReader?
private string[] getNames(string fileName)
{
XmlReader xmlReader = XmlReader.Create(fileName);
List<string> names = new List<string>();
while (xmlReader.Read())
{
//keep reading until we see your element
if (xmlReader.Name.Equals("Keyword") && (xmlReader.NodeType == XmlNodeType.Element))
{
// get attribute from the Xml element here
string name = xmlReader.GetAttribute("name");
// --> now **add to collection** - or whatever
names.Add(name);
}
}
return names.ToArray();
}
Another good option would be the XPathNavigator class - which is faster than XmlDoc and you can use XPath.
Also I would suggest to go with this approach only IFF after you try with the straightforward options you're not happy with performance.
You could use XPath to get all the elements, then a LINQ query to get the values on all the name atttributes you find:
XDocument doc = yourDocument;
var nodes = from element in doc.XPathSelectElements("//Keyword")
let att = element.Attribute("name")
where att != null
select att.Value;
string[] names = nodes.ToArray();
The //Keyword XPath expression means, "all elements in the document, named "Keyword".
Edit: Just saw that you only want elements named Keyword. Updated the code sample.
Like others, I would suggest using LINQ to XML - but I don't think there's much need to use XPath here. Here's a simple method to return all the keyword names within a file:
static IEnumerable<string> GetKeywordNames(string file)
{
return XDocument.Load(file)
.Descendants("Keyword")
.Attributes("name")
.Select(attr => attr.Value);
}
Nice and declarative :)
Note that if you're going to want to use the result more than once, you should call ToList() or ToArray() on it, otherwise it'll reload the file each time. Of course you could change the method to return List<string> or string[] by -adding the relevant call to the end of the chain of method calls, e.g.
static List<string> GetKeywordNames(string file)
{
return XDocument.Load(file)
.Descendants("Keyword")
.Attributes("name")
.Select(attr => attr.Value)
.ToList();
}
Also note that this just gives you the names - I would have expected you to want the other details of the elements, in which case you'd probably want something slightly different. If it turns out you need more, please let us know.
You could use LINQ to XML.
Example:
var xmlFile = XDocument.Load(someFile);
var query = from item in xmlFile.Descendants("childobject")
where !String.IsNullOrEmpty(item.Attribute("using")
select new
{
AttributeValue = item.Attribute("using").Value
};
You'll likely want to use XPath. //Keyword/#name should get you all of the keyword names.
Here's a good introduction: .Net and XML XPath Queries
**<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>**
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);
}
}
}