How to retrieve value from attribute in XML? - c#

What I have:
<?xml version="1.0" encoding="Unicode" standalone="yes"?>
<FiberItems>
<Fiber China="1a" England="1b" Japan="1c" France="1d" Korea="1e"/>
<Fiber China="2a" England="2b" Japan="2c" France="2d" Korea="2e"/>
</FiberItems>
What I want:
1.retrive all the value of "China" into a string array.
2.if a value of China is "1a", retrive all the value of the rest attributes(1b,1c,1d,1e),.
What I do:
1. I write codes for purpose 1 , but never works >_<
XDocument doc = XDocument.Load("/FiberItems.xml");
IEnumerable<string> query = from fiber in doc.Root.Elements("Fiber")
select (string)fiber.Attribute("China");
string[] myString = new string[3];
for (int i = 0; i < 3; i++)
{
foreach (string item in query)
{
myString[i] = (string)item;
}
}
2. for purpose 2, have no idea yet >_<
need helps

You probably shouldn't use an array to collect your data but when you do:
string[] myString = new string[3];
// for (int i = 0; i < 3; i++)
// {
int i = 0;
foreach (string item in query)
{
myString[i] = (string)item;
i += 1;
}
// }
You are doing 3x3 rounds where only 3 are needed.

You can use the following code:
XDocument root = XDocument.Load("/FiberItems.xml");
var attributesChina = root.Elements("FiberItems").Elements("Fiber").Attributes("China");
// attributesChina will contain all the values of china
foreach (XAttribute china in attributesChina)
{
string value = china.value;
}

Check out System.Xml.Linq.
Here's an example to get a list of all the attributes for one element.
var xml = #"<?xml version=""1.0"" encoding=""Unicode"" standalone=""yes""?>
<FiberItems>
<Fiber China=""1a"" England=""1b"" Japan=""1c"" France=""1d"" Korea=""1e""/>
<Fiber China=""2a"" England=""2b"" Japan=""2c"" France=""2d"" Korea=""2e""/>
</FiberItems>";
XDocument doc = XDocument.Parse(xml);
XElement ele = doc.Root.Element("Fiber");
var att = ele.Attributes();

//Load XML element
XElement root = XElement.Parse(File.ReadAllText("/FiberItems.xml"));
//All china attributes (retrieves 1a,2a)
var chinaAttributes= root.Elements().Attributes("China");
//load all rest attributes for china = 1a, loads 1b,1c,1d,1e
var chinaOneARestAttributes = root.Elements().Where(a=>a.Attribute("China")!=null && a.Attribute("China").Value=="1a").Attributes().Select(x=>x.Value).Where(x=>!String.Equals(x,"1a"));
UPDATED for Null Reference Exception. With the data i had tried earlier, i ensured Attribute China was present for all elements.

In XPath:
/FiberItems/Fiber[#China='1a']/#*
gets you all the attributes of Fiber elements with China='1a' and
/FiberItems/Fiber[#China='1a']/#*[local-name()!='China']
gets you the same sequence of attributes excluding the China attribute.

So your requirement is if the attribute China value is "1a" then get all the attribute value of that node. I think it should work
XDocument doc = XDocument.Load("/FiberItems.xml");
IEnumerable<string> query = from fiber in doc.Root.Elements("Fiber")
//condition 1
where (string)fiber.Attribute("China") == "1a"
//condition 2 : Select all except china attribute
select fiber.Attributes().Where(a => a.Name !="China");

Using LINQ to XML:
var xmlStr = #"<?xml version=""1.0"" encoding=""Unicode"" standalone=""yes""?>
<FiberItems>
<Fiber China=""1a"" England=""1b"" Japan=""1c"" France=""1d"" Korea=""1e""/>
<Fiber China=""2a"" England=""2b"" Japan=""2c"" France=""2d"" Korea=""2e""/>
</FiberItems>";
var doc = XDocument.Parse(xmlStr);
var query =
from fiber in doc.Descendants("Fiber")
where (string)fiber.Attribute("China") == "1a"
select String.Join(", ",
(from attr in fiber.Attributes()
where attr.Name != "China"
select (string)attr).ToArray());
This will return a sequence of the other attribute values for each Fiber element that contains a China attribute with the value 1a.

Related

Reading XML data using XmlDocument with C# - Reading attribute data and splitting the results

I'm trying to read some XML data using XmlDocument and having a few challenges.
My issues are as follows:
When I try to get the bfihost value, it gives me the bfihost, propwet and saar element values combined as a single output. Why is this and how do I get just the bfihost?
Each Depth element has an attribute named duration. My first foreach loop writes the string durationString to the console, but it doesn't seem to find any attributes called duration in the XmlNode chldNode.
This issue is probably less important, but when I find the value for the element ReturnPeriods for example I can only retrieve all the values separated by commas. This isn't an issue as I've just used the Split(',') method and assigned it to an array and looped through that. I was curious if XmlDocument had a more elegant way of doing this?
Thanks for any help received.
Here is a stripped down version of the XML data. The , ... have been added as there is a lot more data in the actual xml file.
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<FEHCDROMExportedDescriptors appVersion="2.0.0.0" version="2.0.1">
<PointDescriptors y="111111" x="222222" grid="GB">
<bfihost>0.386</bfihost>
<propwet>0.3</propwet>
<saar>643</saar>
</PointDescriptors>
<PointDDF2013Values>
<ReturnPeriods>
<![CDATA[
1.3, 1.58, 2, ...
]]>
</ReturnPeriods>
<Depths duration="0.083">
<![CDATA[
3.27693489525396, 3.98688804941076, 4.68688804941076, ...
]]>
</Depths>
<Depths duration="0.25">
<![CDATA[
5.37693489525396, 6.51484587430874, 7.81484587430874, ...
]]>
</Depths>
<Depths duration="0.5">
<![CDATA[
6.87693489525396, 8.38688804941076, 10.0017339237195, ...
]]>
</Depths>
</PointDDF2013Values>
</FEHCDROMExportedDescriptors>
The code I have written is as follows:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(file);
XmlTextReader reader = new XmlTextReader(file);
XmlNode node = xmlDoc.ReadNode(reader);
string durationString;
foreach (XmlNode chldNode in node.ChildNodes)
{
//Read the attribute duration
if (chldNode.Name == "Depths")
{
if (chldNode.HasChildNodes)
{
foreach (XmlNode item in node.ChildNodes)
{
durationString = chldNode.Attributes["duration"].Value;
Console.WriteLine("durations[]: " + durationString);
}
}
}
}
XmlNodeList pointDescriptors = xmlDoc.GetElementsByTagName("PointDescriptors");
XmlElement xmlElement = (XmlElement)pointDescriptors[0];
// Get coordinates
XmlAttribute y = xmlElement.GetAttributeNode("y");
Console.WriteLine("y[]: " + y.InnerXml);
XmlAttribute x = xmlElement.GetAttributeNode("x");
Console.WriteLine("x[]: " + y.InnerXml);
// for some reason returns the bfi Host, propwet and saar
XmlNodeList bfihost = xmlDoc.GetElementsByTagName("bfihost");
Console.WriteLine("bfihost[]: " + pointDescriptors[0].InnerText);
// returns all return periods as a string.
XmlNodeList returnPeriods = xmlDoc.GetElementsByTagName("ReturnPeriods");
//Console.WriteLine("Return Periods[]: " + returnPeriods[0].InnerText);
//I can split the string by commas and remove white spaces as follows
string[] returnPeriodsArray = returnPeriods[0].InnerText.Split(',').Select(sValue => sValue.Trim()).ToArray();
foreach (string s in returnPeriodsArray)
{
//System.Console.Write("{0} ", s);
}
int k = 0;
//Loop through all the depths, and split the results
XmlNodeList depths = xmlDoc.GetElementsByTagName("Depths");
XmlAttribute duration;
for (int i = 0; i < depths.Count; i++)
{
if (depths[i].InnerText.Length > 0)
{
System.Console.Write("{0} ", "\n\n" + "Depth xxx" + "\n\n");
string[] depthsArray = depths[i].InnerText.Split(',').Select(sValue => sValue.Trim()).ToArray();
foreach (string s in depthsArray)
{
System.Console.Write("{0} ", "(" + returnPeriodsArray[k] + ") - " + s + "\n");
k++;
if (k > 21)
{
k = 0;
}
}
}
}
And the output I get is:
y[]: 11111 //correct
x[]: 22222 //correct
bfihost[]: 0.3860.3643 //this is combination of three elements for some reason?
///Here I try to get the depth data but nothing is returned.
Depth xxx // xxx is just a place holder until I can fix the above issue.
(1.3) - 3.27693489525396 //all these outputs are correct.
(1.58) - 3.98688804941076
(2) - 4.68688804941076
Depth xxx
(1.3) - 5.37693489525396
(1.58) - 6.51484587430874
(2) - 7.81484587430874
Depth xxx
(1.3) - 6.87693489525396
(1.58) - 8.38688804941076
(2) - 10.0017339237195
It is better to use LINQ to XML API. It is available in the .Net Framework since 2007.
c#
void Main()
{
const string fileName = #"e:\Temp\FEHCDROME.xml";
XDocument xdoc = XDocument.Load(fileName);
XElement xelem = xdoc.Descendants("PointDescriptors").FirstOrDefault();
Console.WriteLine("PointDescriptors:");
Console.WriteLine("y[]: {0}", xelem.Attribute("y").Value);
Console.WriteLine("x[]: {0}", xelem.Attribute("x").Value);
Console.WriteLine("bfihost[]: {0}", xelem.Element("bfihost").Value);
XElement rp = xdoc.Descendants("ReturnPeriods").FirstOrDefault();
Console.WriteLine("{0}ReturnPeriods:", Environment.NewLine);
foreach (string s in rp.Value.Split(',').Select(sValue => sValue.Trim()).ToArray())
{
Console.WriteLine("{0} ", s);
}
Console.WriteLine("{0}Depths:", Environment.NewLine);
foreach (XElement dp in xdoc.Descendants("Depths"))
{
foreach (string s in dp.Value.Split(',').Select(sValue => sValue.Trim()).ToArray())
{
Console.WriteLine("{0} ", s);
}
}
}
Output
PointDescriptors:
y[]: 111111
x[]: 222222
bfihost[]: 0.386
ReturnPeriods:
1.3
1.58
2
...
Depths:
3.27693489525396
3.98688804941076
4.68688804941076
...
5.37693489525396
6.51484587430874
7.81484587430874
...
6.87693489525396
8.38688804941076
10.0017339237195
...

linq separate xml values in child nodes

I want to create a config file to validate userinputs. How do i access the different nodes - the value seems to be every node inside the element. I want to get the separate values to check inputs for length and the right format etc.
C#
var xml = XElement.Load (#"C:\Project\conf\config.xml");
foreach(var child in xml
.Element("Felder").Elements())
{
string cld = child.Name + " " + child.Value;
}
xml:
<?xml version="1.0"?>
<Config>
<Anrede>
<key_1>Testval</key_1>
</Anrede>
<Felder>
<KNR>
<Length>6</Length>
<Format>######</Format>
</KNR>
<AddressStatus>
<Length>1</Length>
<Format>0</Format>
</AddressStatus>
<adressResearch>
<Length>1</Length>
<Format>0</Format>
</adressResearch>
<AnredeNr>
<Length>1</Length>
<Format>0</Format>
</AnredeNr>
</Felder>
</Config>
Output:
KNR 6######
AddressStatus 10
adressResearch 10
AnredeNr 10
Desired Output:
KNR [6,######] or even better KNR.Length = 6, KNR.Format = "######"
Is there a better way to write a config like this?
this linq query (SelectMany in query syntax)
IEnumerable<DictionaryEntry> keyValuePairs =
from child in xml.Element("Felder").Elements()
from tag in child.Elements()
select new DictionaryEntry(String.Format("{0}.{1}", child.Name, tag.Name), tag.Value);
gives output for current xml structure (formatting may be different):
{ Key = KNR.Length, Value = 6 }
{ Key = KNR.Format, Value = ###### }
{ Key = AddressStatus.Length, Value = 1 }
{ Key = AddressStatus.Format, Value = 0 }
{ Key = adressResearch.Length, Value = 1 }
{ Key = adressResearch.Format, Value = 0 }
{ Key = AnredeNr.Length, Value = 1 }
{ Key = AnredeNr.Format, Value = 0 }
try it with a fiddle
Value will return all descendant text nodes, hence when you're getting Value for e.g. KNR it returns 6######.
You need to access the child element values separately:
foreach(var field in xml.Element("Felder").Elements())
{
var fieldName = field.Name.LocalName;
var length = (int)field.Element("Length");
var format = (string)field.Element("Format");
// do what you want with these
// or ...
var childValues = field.Elements().Select(x => x.Value);
var childValuesCommaSeparated = string.Join("," childValues);
}
If you would like to search Xml data for specific node to get its subnodes, you can use Linq to XML. For example:
XDocument xdoc = XDocument.Load(#"C:\Project\conf\config.xml");
string sSetting = "KNF";
string sSetting = "KNR";
var result = xdoc.Descendants("Felder").Descendants()
.Where(x=>x.Name == sSetting)
.Select(x=>new {
Length = x.Element("Length").Value,
Format = x.Element("Format").Value
});
Result:
Length Format
6 ######
In case you want to get every node to be get list of setting's name, length and format, you can try this:
var result1 = xdoc.Descendants("Felder").Elements()
.Select(x=>new
{
Name = x.Name,
Length = x.Element("Length").Value,
Format = x.Element("Format").Value
})
.ToList();
foreach(var setting in result1)
{
Console.WriteLine("{0} | {1} | {2}",
setting.Name, setting.Length, setting.Format);
}

Unable to access inner Xml elements

Question Background:
I have extracted the following inner XML from a larger document:
<Counters total="1" executed="1" passed="1" error="0" failed="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" />
Issue:
Using the following code, I have attempted to access each of the elements of the above XML. I need to extract both the name i.e 'total' and its value '1';
XmlDocument innerXmlDoc = new XmlDocument();
innerXmlDoc.LoadXml(node.InnerXml);
XmlElement element = innerXmlDoc.DocumentElement;
XmlNodeList elements = element.ChildNodes;
for (int i = 0; i < elements.Count; i++)
{
//logic
}
If someone can tell me how to get these values that would be great.
You're iterating though the ChildNodes collection of your element and since the element does not have any, you're iterating through the empty nodelist it gives you.
You want to iterate through the Attributes collection instead:
XmlAttributeCollection coll = element.Attributes;
for (int i = 0; i < coll.Count; i++)
{
Console.WriteLine("name = " + coll[i].Name);
Console.WriteLine("value = " + coll[i].Value);
}
It seems you need a Dictionary. Try using LINQ to XML
var values = new Dictionary<string,string>();
var xmlDocument = XDocument.Load(path);
XNamespace ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
values = xmlDocument
.Descendants(ns + "Counters")
.SelectMany(x => x.Attributes)
.ToDictionary(x => x.Name, x => (string)x));
Managed to solve this myself:
foreach (XmlNode node in nodes)
{
XmlDocument innerXmlDoc = new XmlDocument();
innerXmlDoc.LoadXml(node.InnerXml);
var list = innerXmlDoc.GetElementsByTagName("Counters");
for (int i = 0; i < list.Count; i++)
{
string val = list[i].Attributes["total"].Value;
}
};

Read XML using Linq without element names

I need to read an XML file, I'm trying to use Linq but I'm having some issues getting to the descendants. I need to be able to get the desendants without knowing the element names. Is this possible?
Here is the XML:
<Root>
<myResponse>
<GUID></GUID>
<TType code="1">myTType Value
<TSubType tc="1">TSubType Value</TSubType>
</TType>
<TDate>1999-09-19</TDate>
<TTime>16:00:00.0Z</TTime>
</myResponse>
</Root>
Here is my code:
using (XmlReader nodeReader = XmlReader.Create(#"C:\Projects\GetXML\Test2.xml"))
{
nodeReader.MoveToContent();
XDocument xRoot = XDocument.Load(nodeReader, LoadOptions.SetLineInfo);
foreach (XElement e in xRoot.Elements("Root").DescendantsAndSelf())
Console.WriteLine("{0}{1}{2}",
("".PadRight(e.Ancestors().Count() * 2) + e.Name).PadRight(20), " = " ,
(e.Value).PadRight(5));
}
My results:
Root =
myTType Value
TSubType Value
1999-09-19
16:00:00.0Z
myResponse =
myTType Value
TSubType Value
1999-09-19
16:00:00.0Z
GUID =
TType = myTType Value
TSubType Value
TSubType = TSubType Value
TDate = 1999-09-19
TTime = 16:00:00.0Z
What I am expecting:
Root =
myResponse =
GUID =
TType = myTType Value
TSubType = TSubType Value
TDate = 1999-09-19
TTime = 16:00:00.0Z
Instead of using e.Value, try using:
string.Concat(e.Nodes().OfType<XText>().Select(t => t.Value)).
You could also use:
e.Nodes().OfType<XText>().FirstOrDefault().Value
but then you risk not getting all of the text (if it is split). For example:
<Root>
<myResponse>
<GUID></GUID>
<TType code="1">myTType Value
<TSubType tc="1">TSubType Value</TSubType>
Some more text
</TType>
<TDate>1999-09-19</TDate>
<TTime>16:00:00.0Z</TTime>
</myResponse>
</Root>
With this XML the first piece of code will also include "Some more text", and the second piece won't.
Here is what I did to get the formatted result similar to what you are expecting.
XElement root = XElement.Load("xmlfile1.xml");
var allNodes = root.DescendantNodesAndSelf();
int maxNameLength = allNodes.OfType<XElement>()
.Select(x => x.Name.LocalName.Length)
.Max();
foreach (XNode x in allNodes)
{
XElement node = x as XElement;
if (node != null)
{
int numAncestors = node.Ancestors().Count();
XText textNode = node.Nodes().OfType<XText>().FirstOrDefault();
string text = "";
if (textNode != null)
text = textNode.Value.Trim();
string name = node.Name.LocalName;
// PadLeft() subtracts the string.Length from the padding
// so add its length to the padding amount
string left = name.PadLeft(numAncestors * 5 + name.Length);
// PadRight() wasn't giving the expected results
// so improvised with the following
string right = left + "".PadLeft(maxNameLength + 5 - name.Length);
Console.WriteLine("{0} = {1}", right, text);
}
}
With the result:
Root =
myResponse =
GUID =
TType = myTType Value
TSubType = TSubType Value
TDate = 1999-09-19
TTime = 16:00:00.0Z
Edited to calculate the max name length of all the names, in-case you have a bigger file to run this against and have some really long names.

c# GetElementsByTagName then read the inner tags values how

this below is example xml
<DOC>
<DOCNO>WSJ870323-0180</DOCNO>
<HL>Italy's Commercial Vehicle Sales</HL>
<DD>03/23/87</DD>
<DATELINE>TURIN, Italy</DATELINE>
<TEXT>Commercial-vehicle sales in Italy rose 11.4% in February from a year earlier, to 8,848 units, according to provisional figures from the Italian Association of Auto Makers.</TEXT>
</DOC>
<DOC>
<DOCNO>WSJ870323-0180</DOCNO>
<HL>Italy's Commercial Vehicle Sales</HL>
<DD>03/23/87</DD>
<DATELINE>TURIN, Italy</DATELINE>
<TEXT>Commercial-vehicle sales in Italy rose 11.4% in February from a year earlier, to 8,848 units, according to provisional figures from the Italian Association of Auto Makers.</TEXT>
</DOC>
and this below code is not working why ?
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.Load("docs.xml");
XmlNodeList elemList = doc.GetElementsByTagName("DOC");
for (int i = 0; i < elemList.Count; i++)
{
string docno = elemList[i].Attributes["DOCNO"].ToString();
}
C# 4.0 wpf
Use this code, assuming you have a valid root:
XmlNodeList elemList = doc.GetElementsByTagName("DOC");
for (int i = 0; i < elemList.Count; i++)
{
var elements = elemList[i].SelectNodes("DOCNO");
if (elements == null || elements.Count == 0) continue;
var firstElement = elements.Item(0);
var docno = firstElement.InnerText;
}
Using Linq To Xml to parse Xml is much more easier. For example,
var xDoc = XDocument.Load("docs.xml");
var docs = xDoc.Descendants("DOC")
.Select(x => new{
DocNo = x.Element("DOCNO").Value,
Text = x.Element("TEXT").Value
})
.ToList();

Categories