XDocument get all nodes with attributes - c#

I have the following XML document:
<parameters>
<source value="mysource" />
<name value="myname" />
<id value="myid" />
</parameters>
I'm trying to parse this XML, using XDocument so that I would get a list (Dictionary) containing the node and it's value:
source => mysource,
name => myname,
id => myid
Any ideas on how I can do this?

I tried this out in LINQPad and it provides what you are looking for:
string xml = #"<parameters>
<source value=""mysource"" />
<name value=""myname"" />
<id value=""myid"" />
</parameters>";
var doc = XDocument.Parse(xml);
IDictionary dict = doc.Element("parameters")
.Elements()
.ToDictionary(
d => d.Name.LocalName, // avoids getting an IDictionary<XName,string>
l => l.Attribute("value").Value);

Provided you have a document with the content you show here, this should work:
XDocument doc = ...;
var dict = doc.Root
.Elements()
.ToDictionary(
e => e.Name.ToString(),
e => e.Attribute("value").Value);

XDocument x = XDocument.Parse(
#"<parameters>
<source value=""mysource"" />
<name value=""myname"" />
<id value=""myid"" />
</parameters>");
var nodes = from elem in x.Element("parameters").Elements()
select new { key = elem.Name.LocalName, value = elem.Attribute("value").Value };
var list = new Dictionary<string, string>();
foreach(var node in nodes)
{
list.Add(node.key, node.value);
}

You can use xmldocument/ xmtextreader object use these links these will help
http://msdn.microsoft.com/en-us/library/c445ae5y(v=vs.80).aspx
http://www.c-sharpcorner.com/uploadfile/mahesh/readwritexmltutmellli2111282005041517am/readwritexmltutmellli21.aspx
but i strongly suggest if possible use linq to xml that is far easy and manageable
http://www.codeproject.com/KB/linq/LINQtoXML.aspx

Something like this
XDocument doc = XDocument.Parse(xmlText);
IDictionary<string,string> dic = doc.Elements("parameters").ToDictionary(e => e.Name.LocalName, e => e.Value);
Hope this helps

using System;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
class Program{
static void Main(){
var doc = XDocument.Load("1.xml");
var result = (from node in doc.Root.Elements()
select new{ Key = node.Name, Value = node.Attribute("value").Value})
.ToDictionary(p =>p.Key, p=>p.Value);
foreach(var p in result) Console.WriteLine("{0}=>{1}", p.Key, p.Value);
}
}

Related

Parsing XML element to get value of an element

I have the following XML
<?xml version="1.0" encoding="UTF-8"?> <i18nText><Country code="DE" language="de" text="nach Datum" /><Country code="US" language="en" text="by Date" /></i18nText>
I'm trying to get the value of the text element.There can be different values based on the language element.
How can i populate it into a dictionary ie: value of text for each language .
XmlDocument doc = new XmlDocument();
doc.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?> <i18nText><Country code=\"DE\" language=\"de\" text=\"nach Datum\" /><Country code=\"US\" language=\"en\" text=\"by Date\" /></i18nText>");
XmlNodeList countries = xDoc.GetElementsByTagName("Country");
Dictionary<string, string> dict = new Dictionary<string, string>();
for (int i = 0; i < countries.Count; i++)
{
dict.Add(countries[i].Attributes["language"]?.InnerText, countries[i].Attributes["text"]?.InnerText);
}
var languageToText = XDocument
.Parse(content)
.Descendants("Country")
.Select(p => new
{
language = p.Attribute("language"),
text = p.Attribute("text")
})
.ToDictionary(
p => p.language,
p => p.text);
I would use Linq to Xml, it's more declarative and easier to read than using loops.
using System.Linq;
using System.Xml.Linq;
var xml = #"<?xml version=""1.0"" encoding=""UTF - 8""?> <i18nText><Country code=""DE"" language=""de"" text=""nach Datum"" /><Country code=""DE"" language=""de"" text=""nach Datum"" /><Country code=""US"" language=""en"" text=""by Date"" /></i18nText>";
var doc = XDocument.Parse(xml);
var dic = (from country in doc.Root.Elements()
select new { language=country.Attribute("language").Value, text=country.Attribute("text").Value }).
Distinct().
ToDictionary(country => country.language, country => country.text);

XML Key value paire C#

This is my XDocument
<grantitem adnidtype="306" xmlns="http://tempuri.org/">
<attribute key="AccountNumber" value="1111" />
<attribute key="DateofMeterRead" value="20161226" />
<attribute key="Arrears" value="11.11" />
<attribute key="MeterRead" value="11111" />
</grantitem>
I am trying to read this by using
var q = from b in doc.Descendants("grantitem")
select new
{
key= (string)b.Element("attribute key") ?? tring.Empty,
value= (string)b.Element("value") ?? String.Empty
};
But ist return a null value. Can anyone see some missing?
There are a couple of problems here:
You're trying to fetch elements with a name of grantitem in no namespace, whereas your element is actually in a namespace of http://tempuri.org/
You're trying to retrieve attributes as if they were elements. You need to retrieve the attribute child elements of grantitem and then retrieve the key/value attributes of those elements
Here's an example which does what you want:
using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
var doc = XDocument.Load("test.xml");
XNamespace ns = "http://tempuri.org/";
var query = doc
.Descendants(ns + "grantitem")
.Elements(ns + "attribute")
.Select(x => new {
Key = (string) x.Attribute("key") ?? "",
Value = (string) x.Attribute("value") ?? ""
});
foreach (var item in query)
{
Console.WriteLine(item);
}
}
}
You might consider using creating KeyValuePair<string, string> values instead of using an anonymous type.
Note that this is geared towards being able to find multiple grantitem elements anywhere in the doc. If the reality is that there's always a single grantitem element and it's always the root element, I'd probably use doc.Root.Elements(ns + "attribute") instead of using Descendants first.
I like doing this with a dictionary :
Dictionary<string,string> dict = from b in doc.Descendants("grantitem").FirstOrDefault().Elements("attribute").GroupBy(x => (string)x.Attribute("key"), y => (string)y.Attribute("value"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());

Sort by two attributes from the same element in an xml file

How can I get two attributes from the same element in an xml file, compare them and sort them in numerical order?
The XML elements are:
<Parent>
<Nice to="647429920" from="20200935" />
<Nice to="647431008" from="20200969" />
<Nice to="647432224" from="20201007" />
<Nice to="647437984" from="20201187" />
<Nice to="647441632" from="20201301" />
<Nice to="647441760" from="20201305" />
<Nice to="647443360" from="20201355" />
<Nice to="647445728" from="20201429" />
<Nice to="647446144" from="20201442" />
<Nice to="647447296" from="20201478" />
<Nice to="647450400" from="20201575" />
<Nice to="647450752" from="20201586" />
<Nice to="647451232" from="20201601" />
</Parent>
I've tried to do this as a start to get the attributes:
foreach (XElement node in xDoc.DocumentElement)
{
Console.Write(node.Element("Nice").Attribute("to").Value);
Console.Write(node.Element("Nice").Attribute("from").Value);
Console.WriteLine(node.Element("Entry").Attribute("from").Value);
}
This breaks with a Cast exception.
Edit:
Updated to this:
var xDoc1 = XDocument.Load(xmlPath);
foreach (XElement node in xDoc1.Elements())
{
Console.WriteLine(node.Element("Nice").Attribute("to").Value);
Console.WriteLine(node.Element("Nice").Attribute("from").Value);
}
But the code within the body only gets read once and then the program exits. It does not loop through the entire xml file.
Daniele's Answer Works but there can be some issues and using linq you can skip foreach loops:
First, your values look like integers so using Value without parsing it to an integer can cause problems (IE when comparing strings "10" < "2").
Secondly, there is no null checking to see if an attribute exists, which will throw a null reference exception.
var xDoc1 = XDocument.Load(/* xml path */);
var nodes = xDoc1.Root.Elements("Nice");
var values = nodes
.Where(n => n.Attributes().Any(a => a.Name == "to"))
.Select(n => {
int result;
int.TryParse(n.Attributes().First(a => a.Name == "to").Value, out result);
return result;
})
.ToList();
values.AddRange(nodes
.Where(n => n.Attributes().Any(a => a.Name == "from"))
.Select(n => {
int result;
int.TryParse(n.Attributes().First(a => a.Name == "from").Value, out result);
return result;
})
.ToList());
values = values.OrderBy(n => n).ToList();
Maybe you are trying to cast the 'Xml.XmlElement' returned from the 'DocumentElement' to a 'Linq.XElement'.
Probably you need something like this:
var xDoc = new XmlDocument();
var orderedList = new List<int>();
xDoc.Load(/* xml path */);
var els = xDoc.GetElementsByTagName("Nice").Cast<XmlNode>();
foreach (var el in els)
{
Console.WriteLine(el.Attributes.Item(0).Value);
Console.WriteLine(el.Attributes.Item(1).Value);
}
or this code if you want to use xml.linq
Edited after #Ed Plunkett tips
var xDoc1 = XDocument.Load(/* xml path */);
var nodes = xDoc1.Elements("Parent").Elements("Nice");
if(nodes != null && nodes.Any())
{
foreach (XElement node in nodes)
{
orderedList.Add(int.Parse(node.Attribute("to").Value));
orderedList.Add(int.Parse(node.Attribute("from").Value));
}
}
orderedList.Sort();
foreach (var a in orderedList)
{
Console.WriteLine(a);
}
Console.ReadLine();

C# ASP.NET Sort XML Nodes by custom attribute

Is there a way we can sort xmlnodes based on attribute values? The point is that every child node has a different name, despite it, I want to sort them by attribute.
E.g.
<Doc>
<mar_03 data="03">
<Mattina_Turno_1 />
</mar_03>
<dom_01 data="01">
<Mattina_Turno_1 />
</dom_01>
<mer_04 data="04">
<Mattina_Turno_1 />
<Mattina_Turno_2 />
</mer_04>
</Doc>
Should become
<Doc>
<dom_01 data="01">
<Mattina_Turno_1 />
</dom_01>
<mar_03 data="03">
<Mattina_Turno_1 />
</mar_03>
<mer_04 data="04">
<Mattina_Turno_1 />
<Mattina_Turno_2 />
</mer_04> </Doc>
How can I do it? After sorting obviously I want to overwrite the file.
This answer does not fix my problem since i can not define the node "item" since every my nodes are named differently.
Thanks, and please do not mark it as duplicate, because it is not!
Please try,
XDocument xdoc = XDocument.Load("File.xml");
var result = xdoc.Element("Doc")
.Elements()
.OrderBy(s => (int)s.Attribute("data"));
string xmlOutPut = string.Empty;
result.ToList().ForEach(a =>
{
xmlOutPut += a;
});
Where data and Doc is the parent element is your attribute according to your example. File.xml is your xml file name. You will get the sorted output in 'xmlOutPut'
or everything in a single Linq query,
XDocument xdoc = XDocument.Load("XMLFile2.xml");
string xmlOutPut = string.Empty;
xdoc.Element("Doc")
.Elements()
.OrderBy(s => (int)s.Attribute("data"))
.ToList().ForEach(a =>
{
xmlOutPut += a;
});
Sorting
XDocument xDoc = XDocument.Load("FileName.xml");
var res = xDoc.Element("Doc")
.Elements()
.OrderByDescending(c => (int) c.Attribute("data"));
Then to save it:
XDocument doc = new XDocument(new XElement("Doc", res));
doc.Save("FileName.xml");

Filtering out duplicate XElements based on an attribute value from a Linq query

I'm using Linq to try to filter out any duplicate XElements that have the same value for the "name" attribute.
Original xml:
<foo>
<property name="John" value="Doe" id="1" />
<property name="Paul" value="Lee" id="1" />
<property name="Ken" value="Flow" id="1" />
<property name="Jane" value="Horace" id="1" />
<property name="Paul" value="Lee" id="1" />
... other xml properties with different id's
</foo>
// project elements in group into a new XElement
// (this is for another part of the code)
var props = group.data.Select( f => new XElement("property",
new XAttribute("name", f.Attribute("name").Value), f.Attribute("value"));
// filter out duplicates
props = props.Where(f => f.ElementsBeforeSelf()
.Where(g => g.Attribute("name").Value ==
f.Attribute("name").Value)
.Count() == 0);
Unfortunately, the filter step isnt working. I would think that Where() filter would check for any element before the current one that has the same property name and then include that in a set that was more than zero, thereby excluding the current element (called 'f'), but thats not happening. Thoughts ?
You could just create an IEqualityComparer to use with the Distinct(), that should get you what you need.
class Program
{
static void Main(string[] args)
{
string xml = "<foo><property name=\"John\" value=\"Doe\" id=\"1\"/><property name=\"Paul\" value=\"Lee\" id=\"1\"/><property name=\"Ken\" value=\"Flow\" id=\"1\"/><property name=\"Jane\" value=\"Horace\" id=\"1\"/><property name=\"Paul\" value=\"Lee\" id=\"1\"/></foo>";
XElement x = XElement.Parse(xml);
var a = x.Elements().Distinct(new MyComparer()).ToList();
}
}
class MyComparer : IEqualityComparer<XElement>
{
public bool Equals(XElement x, XElement y)
{
return x.Attribute("name").Value == y.Attribute("name").Value;
}
public int GetHashCode(XElement obj)
{
return obj.Attribute("name").Value.GetHashCode();
}
}
Your appoach is a bit weird, e.g., You don't need to project elements into new elements; it just works(tm) when you add existing elements to a new document.
I would simply group the <property> elements by the name attribute and then select the first element from each group:
var doc = XDocument.Parse(#"<foo>...</foo>");
var result = new XDocument(new XElement("foo",
from property in doc.Root
group property by (string)property.Attribute("name") into g
select g.First()));
I think you should remove the duplicates first, and then do your projection. For example:
var uniqueProps = from property in doc.Root
group property by (string)property.Attribute("name") into g
select g.First() into f
select new XElement("property",
new XAttribute("name", f.Attribute("name").Value),
f.Attribute("value"));
or, if you prefer method syntax,
var uniqueProps = doc.Root
.GroupBy(property => (string)property.Attribute("name"))
.Select(g => g.First())
.Select(f => new XElement("property",
new XAttribute("name", f.Attribute("name").Value),
f.Attribute("value")));

Categories

Resources