C# how can I get all elements name from a xml file - c#

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];
}
}

Related

How do I read xml in C#? [duplicate]

How can I read an XML attribute using C#'s XmlDocument?
I have an XML file which looks somewhat like this:
<?xml version="1.0" encoding="utf-8" ?>
<MyConfiguration xmlns="http://tempuri.org/myOwnSchema.xsd" SuperNumber="1" SuperString="whipcream">
<Other stuff />
</MyConfiguration>
How would I read the XML attributes SuperNumber and SuperString?
Currently I'm using XmlDocument, and I get the values in between using XmlDocument's GetElementsByTagName() and that works really well. I just can't figure out how to get the attributes?
XmlNodeList elemList = doc.GetElementsByTagName(...);
for (int i = 0; i < elemList.Count; i++)
{
string attrVal = elemList[i].Attributes["SuperString"].Value;
}
You should look into XPath. Once you start using it, you'll find its a lot more efficient and easier to code than iterating through lists. It also lets you directly get the things you want.
Then the code would be something similar to
string attrVal = doc.SelectSingleNode("/MyConfiguration/#SuperNumber").Value;
Note that XPath 3.0 became a W3C Recommendation on April 8, 2014.
You can migrate to XDocument instead of XmlDocument and then use Linq if you prefer that syntax. Something like:
var q = (from myConfig in xDoc.Elements("MyConfiguration")
select myConfig.Attribute("SuperString").Value)
.First();
I have an Xml File books.xml
<ParameterDBConfig>
<ID Definition="1" />
</ParameterDBConfig>
Program:
XmlDocument doc = new XmlDocument();
doc.Load("D:/siva/books.xml");
XmlNodeList elemList = doc.GetElementsByTagName("ID");
for (int i = 0; i < elemList.Count; i++)
{
string attrVal = elemList[i].Attributes["Definition"].Value;
}
Now, attrVal has the value of ID.
XmlDocument.Attributes perhaps? (Which has a method GetNamedItem that will presumably do what you want, although I've always just iterated the attribute collection)
Assuming your example document is in the string variable doc
> XDocument.Parse(doc).Root.Attribute("SuperNumber")
1
If your XML contains namespaces, then you can do the following in order to obtain the value of an attribute:
var xmlDoc = new XmlDocument();
// content is your XML as string
xmlDoc.LoadXml(content);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
// make sure the namespace identifier, URN in this case, matches what you have in your XML
nsmgr.AddNamespace("ns", "urn:oasis:names:tc:SAML:2.0:protocol");
// get the value of Destination attribute from within the Response node with a prefix who's identifier is "urn:oasis:names:tc:SAML:2.0:protocol" using XPath
var str = xmlDoc.SelectSingleNode("/ns:Response/#Destination", nsmgr);
if (str != null)
{
Console.WriteLine(str.Value);
}
More on XML namespaces here and here.

Reading certain nodes from elements one at a time of xml file in c#

I'm trying to read through .xml -file and get information out of there. Here is a sample of the .xml -file I have:
<?xml version="1.0" encoding="UTF-8"?>
<XmlFile>
<xmlsource>
<Name>TestXml</Name>
<filename>MyXmlFile.xml</filename>
<Information Key="GeneralInfo"/>
<Products>
<Product>
<ProductName>Product1</ProductName>
<Name Key="SomeName"/>
<Usages>
<Usage>
<Specs>
<Spec1 Key="Moving"/>
<Spec2 Key="Lifting"/>
</Specs>
<Info1>
<MovingInfo1>yes</MovingInfo1>
</Info1>
<Info2>Noup</Info2>
<MoreSpecs>
<ProductModel1>
<DetInfo1>DetInfo1</DetInfo1>
<DetInfo2>DetInfo2</DetInfo2>
</ProductModel11>
</MoreSpecs>
</Usage>
</Usages>
</Product>
<Product>
<ProductName>Product2</ProductName>
<Name Key="SomeName2"/>
<Usages>
<Usage>
<Specs>
<Spec1 Key="Moving"/>
<Spec2 Key="Lifting"/>
</Specs>
<Info1>
<MovingInfo1>not</MovingInfo1>
</Info1>
<Info2>Yes</Info2>
<MoreSpecs>
<ProductModel1>
<DetInfo1>DetInfo1</DetInfo1>
</ProductModel1>
</MoreSpecs>
</Usage>
<Usage>
<Specs>
<Spec1 Key="Turning"/>
</Specs>
<Info1>
<TurningInfo1>Infoooo</TurningInfo1>
</Info1>
<Info2>No</Info2>
<MoreSpecs>
<ProductType1>
<DetInfo1>DetInfo1</DetInfo1>
</ProductType1>
</MoreSpecs>
</Usage>
</Usages>
</Product>
</Products>
</xmlsource>
(This is just a sample, original file has a lot more data in it.)
I want to know only the values of ProductName and Spec1. As you can see from the sample, 'Product2' has two different values of Spec1: 'Moving' and 'Turning'.
What I'm trying to achieve:
Read ProductName ("Product1") from the first <Product> and then the Spec1 ("Moving"), then do something with the information. After that, move to next <Product>, read ProductName ("Product2"), Spec1 ("Moving") and the other Spec1 ("Turning"), and skipping all the other possible Spec values - meaning, that I want only Spec1 value. And so on go through the hole file.
Here is what I have tried to do:
public void getNodes(string filepath)
{
xmlFilePath = filepath;
XmlDocument xDoc = new XmlDocument();
xDoc.Load(xmlFilePath);
XmlNodeList products = xDoc.SelectNodes("//Product");
XmlNodeList productnames = xDoc.SelectNodes("//Product/ProductName");
XmlNodeList specs = xDoc.SelectNodes("//Product//Spec1");
AllocConsole();
Console.WriteLine(products.Count);
Console.WriteLine(specs.Count);
foreach (XmlNode xn in specs)
{
XmlAttributeCollection spec1Atts = xn.Attributes;
Console.WriteLine(spec1Atts["Key"].Value.ToString());
}
for (int i = 0; i < products.Count; i++)
{
Console.WriteLine(products.Item(i).InnerText);
Console.ReadLine();
}
}
This is the closest I have got (closest to what I'm trying to do).
There, first I have load the .xml -file.
Then, in lines containing XmlNodeList etc. I'm filtering with those requirements.
Here (below), is being checked the amount of products specs:
Console.WriteLine(products.Count);
Console.WriteLine(specs.Count);
Finally I'm printing out the values which has been read. With this, the print-out is obviously:
First comes out the amounts
Second comes the specs
And finally the productnames
As said above, I want ProductNames and Spec1's to be "linked" together.
I tried many methods e.g. shown in here: Reading multiple child nodes of xml file
Somehow I couldn't make any example work in my situation. Maybe it's because in my case, there is so deep parent-child pairs?
I can't change the structure of the .xml -file. If I could, I would have changed it already...
So, my question is: Could someone show me a hint/way how to achieve my goal? Thanks in advance.
What you need to do is traverse the hierarchy. In the revised code below, I find the ProductName, then within that, I look for the next node and so on until I find the Specs that correspond to that product.
private static void getNodes(string filePath)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
var productNodes = xmlDoc.SelectNodes("//Product");
if (productNodes != null)
{
foreach (XmlNode product in productNodes)
{
var childNodes = product.ChildNodes;
foreach (XmlNode child in childNodes)
{
if (child.Name == "ProductName")
{
Console.WriteLine(child.InnerText);
}
else if (child.Name == "Usages")
{
var childNodes2 = child.ChildNodes;
foreach (XmlNode child2 in childNodes2)
{
if (child2.Name == "Usage")
{
var childNodes3 = child2.ChildNodes;
foreach (XmlNode child3 in childNodes3)
{
if (child3.Name == "Specs")
{
var childNodes4 = child3.ChildNodes;
foreach (XmlNode child4 in childNodes4)
{
foreach (XmlNode a in child4.Attributes)
{
Console.WriteLine($" {a.InnerText}");
}
}
}
}
}
}
}
}
}
}
Console.ReadLine();
}
Hope that helps. If so, please vote for my answer because I need the reputation. Thank you.
If I understand your question correctly, you are trying to retrieve the specs for each product and you want to use them together. (Apologies if I don't fully get it). if that is the case you can try probing the product element directly in your example loop. like
for (int i = 0; i < products.Count; i++) {
var specs = products[i].SelectNodes("Usages/Usage/Specs")[0].ChildNodes;
for (int j = 0; j < specs.Count; j++)
Console.WriteLine("{0}->{1}", products[i].FirstChild.InnerText, specs[j].Attributes["Key"].Value);
}
I hope this helps

How to use LINQ to XML when XML parent and child nodes have the same name

I am trying to extract some SQL data to XML from a Microsoft Dynamics environment, I am currently using LINQ To XML in C# to read and write to my XML files. One piece of data I need is from a view called SECURITYSUBROLE. Looking at the structure of this view shows that there is a column also named SECURITYSUBROLE. My normal method of extraction has given me this XML.
<SECURITYSUBROLE>
<SECURITYROLE>886301</SECURITYROLE>
<SECURITYSUBROLE>886317</SECURITYSUBROLE>
<VALIDFROM>1900-01-01T00:00:00-06:00</VALIDFROM>
<VALIDFROMTZID>0</VALIDFROMTZID>
<VALIDTO>1900-01-01T00:00:00-06:00</VALIDTO>
<VALIDTOTZID>0</VALIDTOTZID>
<RECVERSION>1</RECVERSION>
<RECID>886317</RECID>
</SECURITYSUBROLE>
When I try to import this data later on, I am getting errors because the parent XML node has the same name as a child node. Here is a snippet of the import method:
XmlReaderSettings settings = new XmlReaderSettings();
settings.CheckCharacters = false;
XmlReader reader = XmlReader.Create(path, settings);
reader.MoveToContent();
int count = 1;
List<XElement> xmlSubset = new List<XElement>();
while (reader.ReadToFollowing(xmlTag))
{
if (count % 1000 == 0)
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
XDocument xmlTemp = new XDocument(new XElement(xmlTag));
foreach (XElement elem in xmlSubset)
{
xmlTemp.Root.Add(elem);
}
xmlSubset = new List<XElement>();
ImportTableByName(connectionString, tableName, xmlTemp);
count = 1;
}
else
{
xmlSubset.Add(XElement.Load(reader.ReadSubtree()));
count++;
}
}
}
It's currently failing on the XmlReader.ReadToFollowing, where it doesn't know where to go next because of the name confusion. So my question has two parts:
1) Is there some better way to be extracting this data other than to XML?
2) Is there a way through LINQ To XML that I can somehow differentiate between the parent and child nodes named exactly the same?
To get the elements (in your case) for SECURITYSUBROLE you can check to see if the element's have children:
XElement root = XElement.Load(path);
var subroles = root.Descendants("SECURITYSUBROLE") // all subroles
.Where(x => !x.HasElements); // only subroles without children
I'm going to suggest a different approach:
1) VS2013 (possibly earlier versions too) has a function to create a class from an XML source. So get one of your XML files and copy the content to your clipboard. Then in a new class file Edit --> Paste Special --> Paste XML as Classes
2) Look into XmlSerialization which will allow you to convert an XML file into an in memory object with a strongly typed class.
XmlSerializer s = new XmlSerializer(yourNewClassTYPE);
TextReader r = new StreamReader(XmlFileLocation);
var dataFromYourXmlAsAStronglyTypedClass = (yourNewlyClassTYPE) s.Deserialize(r);
r.Close();

Get XmlNodeList if a particular element value or its attribute value is present in a given list of strings

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.

How to read XML attributes in C#?

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);
}
}
}

Categories