C# XDocument Read all Nodes from XML-File - c#

I have an XML-file like this:
<Inventory>
<Item>
<Name>Super Mario Bros</Name>
<Count>14</Count>
<Price>29,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1985</Year>
<ProductID>001</ProductID>
</Item>
<Item>
<Name>The Legend of Zelda</Name>
<Count>12</Count>
<Price>34,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1986</Year>
<ProductID>002</ProductID>
</Item>
<Item>
<Name>Street Fighter</Name>
<Count>82</Count>
<Price>19,99</Price>
<Comment><No Comments on this Product></Comment>
<Artist>N/A</Artist>
<Publisher>Nintendo</Publisher>
<Genre>Video Games</Genre>
<Year>1987</Year>
<ProductID>003</ProductID>
</Item>
</Inventory>
(There are more Items, but they are all the same, except for the values.)
Now I want to iterate through each Item and extract every value from each node. Here's what I've tried so far:
var xDocument = XDocument.Load(FilePath_CSVToXML);
string xml = xDocument.ToString();
StringBuilder sb = new StringBuilder();
foreach (XElement xe in xDocument.Descendants("Inventory")) {
sb.Append(xe);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
The code above properly displays the XML-file, but it keeps the Nodes. (Name, Count, Price, etc.) I only want the values.

You need to use the Value property, i.e. sb.Append(xe.Value).

Try code below. The innertag of Comment doesn't need angle brackets so remove.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication49
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("Item").Select(x => new {
name = (string)x.Element("Name"),
count = (int)x.Element("Count"),
price = (decimal)x.Element("Price"),
comment = (string)x.Element("Comment"),
artist = (string)x.Element("Artist"),
publisher = (string)x.Element("Publisher"),
genre = (string)x.Element("Genre"),
year = (int)x.Element("Year"),
productID = (string)x.Element("ProductID")
}).ToList();
}
}
}

Related

Looping through items and read desired elements

I have something like this:
<ITEMS ASOF_DATE="6/2/2022" RECORDS="1" CREATE_DATE="6/3/2022" >
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomeValue>-808</SomeValue>
</ITEM>
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomeValue>-808</SomeValue>
<SomeOtherValue>-808</SomeOtherValue>
</ITEM>
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomethingElse>234</SomethingElse>
</ITEM>
</ITEMS>
It can have multiple <ITEM> items and those can have multiple elements under it for example first item has three elements, second one has four , third one has three in this example.
I want to loop through all of these <ITEM> items and read some specific elements that I am interested in. For example <port_name> and <bench_name>
But I can't figure out how to do the loop. For example root.SelectNodes("ITEMS") in my code below isn't what I am hoping to be able to loop through its items.
XmlDocument doc = new XmlDocument();
doc.Load(myFilePath.ToString());
XmlElement root = doc.DocumentElement;
var lineItems = root.InnerXml;
XmlNodeList nodes = root.SelectNodes("ITEMS");
foreach (XmlNode node in nodes)
{
var somethingElse = node.SelectNodes("ITEM");
}
Try using the Elements() or Descendants() methods to iterate your root element.
Edited in response to comment:
If you're looking for something specific, I have found that one of the easiest ways to do this is to write an Extension Method for XElement that suites your requirements. Here is an example.
static class Extensions
{
public static bool IsPortNameMatch(this XElement xElement, string name)
{
if (!string.Equals(xElement.Name.LocalName, "ITEM")) return false;
var portName = xElement.Element("port_name");
if (portName == null) return false;
return string.Equals((string)portName, name);
}
}
To test the extension, I have added a fourth ITEM to the source with a port_name of "UniquePortName". The new console output now reports this as "Found".
static void Main(string[] args)
{
var root = XElement.Parse(source);
foreach (var item in root.Elements("ITEM"))
{
foreach (var entry in item.Elements())
{
Console.WriteLine($"{entry.Name.LocalName} = {(string)entry}");
}
}
// Find something specific
var matchItem =
root.Descendants("ITEM")
.First(match => match.IsPortNameMatch("UniquePortName"));
Console.WriteLine($"FOUND:{Environment.NewLine}{matchItem.ToString()}");
}
const string source =
#"<ITEMS ASOF_DATE=""6/2/2022"" RECORDS=""1"" CREATE_DATE=""6/3/2022"" >
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomeValue>-808</SomeValue>
</ITEM>
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomeValue>-808</SomeValue>
<SomeOtherValue>-808</SomeOtherValue>
</ITEM>
<ITEM>
<port_name>95512M</port_name>
<bench_name>LEHSECUR</bench_name>
<SomethingElse>234</SomethingElse>
</ITEM>
<ITEM>
<port_name>UniquePortName</port_name>
<bench_name>LEHSECUR</bench_name>
<SomethingElse>234</SomethingElse>
</ITEM>
</ITEMS>";
}
Using XML Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Data;
namespace ConsoleApplication23
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
foreach (XElement item in doc.Descendants("ITEM"))
{
foreach (XElement child in item.Elements())
{
Console.WriteLine("{0} = {1}", child.Name.LocalName, (string)child);
}
}
Console.ReadLine();
}
}
}

Get Each Value on an XML Node

I have an XML that looks like this:
<HiddenTopicsValues TopicCodes="TopicValues">
<Topic>
<Code>topic_aboutme</Code>
<Value>1</Value>
</Topic>
<Topic>
<Code>topic_aboutyou</Code>
<Value>1</Value>
</HiddenTopicsValues>
My goal is to create a Dictionary that lets the <Code> act as the Key, and <Value> as the Value (of the Dictionary). I declared the dictionary as shown below:
Dictionary<string,int> dictionary_topics = new Dictionary<string,int>();
And used a for loop to iterate all the values in the XML:
// Load XML Document
XmlDocument xmlTopics = new XmlDocument();
xmlTopics.Load(path);
// Get All Hidden Topics
XmlNodeList ndTopics = xmlTopics.GetElementsByTagName("Topic");
for (int i = 0; i < ndTopics.Count; i++)
{
string _topicCode = ndTopics[i].InnerText[0].ToString();
int _topicValue = ndTopics[i].InnerText[1].ToString();
// Add Topic to Dictionary
dictionary_topics.Add(_topicCode, _topicValue);
}
I thought that this: ndTopics[i].InnerText[0] would return this: topic_aboutme
And this: ndTopics[0].InnerText[1] would return this: 1
Based on the given XML.
I tried displaying ndTopics[0].InnerText and it shows this:
topic_aboutme1
How can I separate the topic_aboutme(<Code>) and 1(<Value>)?
Forgive my naiveness, I'm not really used in utilising XML.
Real easy in Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, string> dict = doc.Descendants("Topic")
.GroupBy(x => (string)x.Element("Code"), y => (string)y.Element("Value"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}

Find word in string and replace it

I have a string [XML], I want to find tags that contain a word and replace that tag with another name.
I have tried using this, with no success:
var regex = new Regex(#"\bKeyValueOfstringOutcome\b", RegexOptions.IgnoreCase);
string result = regex.Replace(xml.Document.ToString(), "");
This is my XML:
<Response>
<Outcome>
<KeyValueOfstringOutcomeTest1>
<Key>Icon</Key>
<Value>
<DataType>System.String</DataType>
<Field>Icon</Field>
<Value>O</Value>
</Value>
</KeyValueOfstringOutcomeTest1>
<KeyValueOfstringOutcomeTest2>
<Key>IconDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconDescription</Field>
<Value>Old</Value>
</Value>
</KeyValueOfstringOutcomeTest2>
<KeyValueOfstringOutcomeTest3>
<Key>IconLongDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconLongDescription</Field>
<Value>Older</Value>
</Value>
</KeyValueOfstringOutcomeTest3>
</Outcome>
</Response>
I need to find the nodes that contain KeyValueOfstringOutcome, and replace KeyValueOfstringOutcomeTest1, KeyValueOfstringOutcomeTest2, KeyValueOfstringOutcomeTest3 with KeyValueOfstringOutcome.
If I understood you correctly that you want to get rid of Test1, Test2 and so on.
I would suggest to use this Replace method.
string replacedXML = Regex.Replace(xml.Document.ToString(),
#"KeyValueOfstringOutcomeTest\d+",
"KeyValueOfstringOutcome");
\d+ will match 1 or more occurences of a digit behind your keyword
I wouldn´t use REGEX to match and change the name of the nodes.
If you ever update your file or namings it´s going to crash or change some other node which contains the buzzword, too.
So I would create a loop through each childnode at the lvl/hierarchy you want to change and then grap it and rename it.
foreach (XmlNode node in doc.ChildNodes)
{
// [2] should be the the deepth of childenotes named `KeyValueOfstringOutcomeTestX`
node.ChildNode[2].Name = "YOUR NEW NAME TO THIS NODE";
}
doc.Save(); // Change renamed nodes to the document.
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication107
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> keyValueOfstringOutcomeTests = doc.Descendants().Where(x => x.Name.LocalName.StartsWith("KeyValueOfstringOutcomeTest")).ToList();
foreach (XElement keyValueOfstringOutcomeTest in keyValueOfstringOutcomeTests)
{
keyValueOfstringOutcomeTest.ReplaceWith(new XElement("KeyValueOfstringOutcomeTest", keyValueOfstringOutcomeTest.Elements()));
}
}
}
}
Try this if it help.
string result = Regex.Replace(xml.Document.ToString(), "[a-zA-Z0-9]*KeyValueOfstringOutcome[a-zA-Z0-9]*","KeyValueOfstringOutcome");
This is what I've done to accomplish this.
var match = Regex.Match(xml.Document.ToString(), #"KeyValueOfstringOutcome.*>");
if (match.Success)
{
var replacedText = Regex.Replace(xml.Document.ToString(), #"KeyValueOfstringOutcome.*>", "KeyValueOfstringOutcome>");
}

Unable to get list from xml using xPathNavigator

List<string> list = new List<string>();
foreach (XPathNavigator node in nav.Select("configuration/company/work/worktime"))
{
string day = getAttribute(node, "day");
string time = getAttribute(node, "time");
string worktype = ?? // how to get worktype attribute valuefrom parent node
list.Add(day,time,worktype); // add to list
}
</configuration>
<company>
<work worktype="homeWork">
<worktime day="30" time="10:28"></worktime>
<worktime day="25" time="10:50"></worktime>
</work>
<work worktype="officeWork">
<worktime day="12" time="09:28"></worktime>
<worktime day="15" time="12:28"></worktime>
</work>
</company>
</configuration>
need output as :
list[0] = homeWork,30,10:28
list[1] = homeWork,25,10:50
list[2] = officeWork,12,09:28
list[3] = officeWork,15,12:28
I am trying to get the list from XML but failed to get output like given above (using xpath navigator, how can I access parent node to get worktype attribute, and other remaining inner node attribute?
I'd suggest using LINQ to XML over XPath, but if you must use XPathNavigator then you need to iterate each work element followed by each of its worktime child elements. This way you can use the worktype from the parent context:
foreach (XPathNavigator work in nav.Select("configuration/company/work"))
{
var workType = work.GetAttribute("worktype", string.Empty);
foreach (XPathNavigator worktime in work.Select("worktime"))
{
var day = worktime.GetAttribute("day", string.Empty);
var time = worktime.GetAttribute("time", string.Empty);
list.Add($"{workType}, {day}, {time}");
}
}
See this fiddle for a working demo.
Use a nested loop. Initially retrieve the work nodes with configuration/company/work. Retrieve the worktype attribute and store in a variable. Then loop through the child worktype nodes and add a string to the list for each one
Use Net Library enhanced xml (linq xml)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("work").Select(x => new {
worktype = (string)x.Attribute("worktype"),
worktime = x.Elements("worktime").Select(y => new {
day = (int)y.Attribute("day"),
time = (DateTime)y.Attribute("time")
}).ToList()
}).ToList();
}
}
}

Get specific attribute of XmlNode

I have an XmlNode, and its OuterXml is the next code I have posted. I need to know how to get The name and the age for each Campaign.
XmlNode Response = client.GetNamesAndAges(xmlRequest);
<Example>
<FromDate>12-05-2016</FromDate>
<ToDate>25-05-2016</ToDate>
<Campaigns>
<Campaign>
<Name>A</Name>
<age>2</age>
</Campaign>
<Campaign>
<Name>B</Name>
<age>1</age>
</Campaign>
</Campaigns>
<Status></Status>
</Example>
You can use XPath via SelectNodes() to get specific nodes/elements i.e Campaign elements in this case, and then print Name and age value from each Campaign :
var campaignList = Response.SelectNodes("Campaigns/Campaign");
foreach(XmlNode campaign in campaignList)
{
Console.WriteLine(campaign["Name"].InnerText);
Console.WriteLine(campaign["age"].InnerText);
}
BTW, Name and age are elements. Attributes in XML is used to reference something else i.e bar is the name of the attribute which value is baz in the following XML element <foo bar="baz"/>.
Use xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var campaign = doc.Descendants("Campaign").Select(x => new
{
name = (string)x.Element("Name"),
age = (int)x.Element("age")
}).ToList();
}
}
}

Categories

Resources