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());
Related
My XML is structured like this:
<Database>
<Member>
<Name>PersonA</Name>
<Rank>RankIWant</Rank>
</Member>
<Member>
<Name>PersonB</Name>
<Rank>RankIDontWant</Rank>
</Member>
</Database>
I have the <Name> value of PersonA, and I want "RankIWant", but I'm not sure how to get it with the information that I have. What do I do?
You can use an XPath query. For your example:
//Member[Name = "PersonA"]/Rank
Fiddle: https://dotnetfiddle.net/udobyd
That XPath query means, select all (the // means all, independent of the position) Rank nodes who have a parent Member node which happens to have a Name descendant with the value PersonA.
You could also use Linq to Xml if you are used to Linq. In this case, the equivalent code would be:
var nodes = XElement.Parse(xml).Descendants("Rank")
.Where(x => x.Parent.Descendants("Name").Any(y => y.Value == "PersonA"));
Fiddle here: https://dotnetfiddle.net/bOVOy7
For your specific query, I'd prefer the XPath version, but your choice.
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
{
static void Main(string[] args)
{
string xml =
"<Database>" +
"<Member>" +
"<Name>PersonA</Name>" +
"<Rank>RankIWant</Rank>" +
"</Member>" +
"<Member>" +
"<Name>PersonB</Name>" +
"<Rank>RankIDontWant</Rank>" +
"</Member>" +
"</Database>";
XElement database = XElement.Parse(xml);
XElement query = database.Elements("Member")
.Where(x => x.Element("Rank").Value == "RankIWant").FirstOrDefault();
}
}
}
Here is another method.
private static void Main()
{
string myRankValue = "";
var xml = #"<Database>
<Member>
<Name>PersonA</Name>
<Rank>RankIWant</Rank>
</Member>
<Member>
<Name>PersonB</Name>
<Rank>RankIDontWant</Rank>
</Member>
</Database>";
var xDoc = XDocument.Parse(xml);
var firstMember =
xDoc.Descendants("Member")
.Where(d => d.Descendants("Name").First().Value == "PersonA")
.Descendants("Rank")
.FirstOrDefault();
if (firstMember != null)
{
myRankValue = firstMember.Value;
}
Console.WriteLine(myRankValue);
}
}
I would like to fetch the string mentioned in 'Value' for various parameter available under 'Name' using c#.
Here is my current xml as follows:
<DrWatson>
<Sets>
<Set>
<APIParameters>
<Parameter Name="SID_STAGE" Value="101198" Required="true" />
<Parameter Name="SID_QE" Value="Test 91817" Required="true" />
</APIParameters>
</Set>
</Sets>
</DrWatson>
I would like to fetch the '101198' available under 'Value' for Name = SID_STAGE.
Please suggest how can i perform it.
You can parse parameters dictionary (that is natural way to store key-value pairs) with LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
var parameters = xdoc.Descendants("Parameter")
.ToDictionary(p => (string)p.Attribute("Name"),
p => (string)p.Attribute("Value"));
var stage = parameters["SID_STAGE"];
Keep in mind, that you should check if parameter exists in dictionary before getting it (if it is possible that parameter can not be in your xml):
if (parameters.ContainsKey("SID_STAGE"))
// get parameter value
Also with XPath you can make query more concrete (if it is possible that somewhere will be another Parameter elements):
var xpath = "DrWatson/Sets/Set/APIParameters/Parameter";
var parameters = xdoc.XPathSelectElements(xpath)
.ToDictionary(p => (string)p.Attribute("Name"),
p => (string)p.Attribute("Value"));
var result = XElement.Parse(xmlString)
.Descendants("Parameter")
.First(node => (string)node.Attribute("Name") == "SID_STAGE")
.Attribute("Value");
Console.WriteLine(result.Value); //prints 101198
Will throw an exception of element with this attribute is absent. Consider using FirstOrDefault if you would like another behaviour.
Use a LINQ to XML query:
var xml = XDocument.Load("path...");
var foo = (from n in xml.Descendants("APIParameters")
where n.Element("Parameter").Attribute("Name").Value == "SID_STAGE"
select n.Element("Parameter").Attribute("Value").Value).FirstOrDefault();
Gives:
101198
using System;
using System.Xml.Linq;
using System.Web;
namespace YourProjectName
{
public static class XmlFileRetrieve
{
public static string GetParameterValue(string name)
{
try
{
string path = HttpContext.Current.Server.MapPath("~/YourFolderName/YourXmlFileName.xml");
XDocument doc = XDocument.Load(path);
if (!(doc == null))
{
var parameter = (from el in doc.Root.Elements("Parameter")
where (string)el.Attribute("Name") == name
select (string)el.Attribute("value")).Select(keyvalue => new { name = keyvalue }).Single(); ;
return parameter.name;
}
return "";
}
catch (Exception e)
{string error=e.Message;return "";
}
}
}
}
I have one custom config file.
<Students>
<student>
<Detail Name="abc" Class="1st Year">
<add key="Main" value="web"/>
<add key="Optional" value="database"/>
</Detail>
</student>
</Students>
I read this file through the IConfigurationHandler interface implementation.
When I read the childNode attributes of Detail element. It return me below result into Immediate Window of IDE.
elem.Attributes.ToObjectArray()
{object[2]}
[0]: {Attribute, Name="key", Value="Main"}
[1]: {Attribute, Name="value", Value="web"}
When I try to write on Console
Console.WriteLine("Value '{0}'",elem.Attributes.ToObjectArray());
it does return me
Value : 'System.Configuration.ConfigXmlAttribute'
elem.Attributes.Item(1) method gives me the Name and Value detail but here I need to pass the index value of attribute which I don't know currently.
I want to get Name and value of attribute through LINQ query and individual display on Console for each childNode attribute as follows:
Value : Name="Key" and Value="Main"
Name="value", Value="web"
How can I achieve that?
If you want to use this Xml Library you can get all the students and their details with this code:
XElement root = XElement.Load(file); // or .Parse(string)
var students = root.Elements("student").Select(s => new
{
Name = s.Get("Detail/Name", string.Empty),
Class = s.Get("Detail/Class", string.Empty),
Items = s.GetElements("Detail/add").Select(add => new
{
Key = add.Get("key", string.Empty),
Value = add.Get("value", string.Empty)
}).ToArray()
}).ToArray();
Then to iterate over them use:
foreach(var student in students)
{
Console.WriteLine(string.Format("{0}: {1}", student.Name, student.Class));
foreach(var item in student.Items)
Console.WriteLine(string.Format(" Key: {0}, Value: {1}", item.Key, item.Value));
}
You can use a Linq Select and string.Join to get the output you want.
string.Join(Environment.NewLine,
elem.Attributes.ToObjectArray()
.Select(a => "Name=" + a.Name + ", Value=" + a.Value)
)
This will get all the attributes of the children of the Detail element as you state in your question.
XDocument x = XDocument.Parse("<Students> <student> <Detail Name=\"abc\" Class=\"1st Year\"> <add key=\"Main\" value=\"web\"/> <add key=\"Optional\" value=\"database\"/> </Detail> </student> </Students>");
var attributes = x.Descendants("Detail")
.Elements()
.Attributes()
.Select(d => new { Name = d.Name, Value = d.Value }).ToArray();
foreach (var attribute in attributes)
{
Console.WriteLine(string.Format("Name={0}, Value={1}", attribute.Name, attribute.Value));
}
If you have the Attributes in an object[] as you wrote, which can be mocked by
var Attributes = new object[]{
new {Name="key", Value="Main"},
new {Name="value", Value="web"}
};
then the issue is that you have anonymous types whose names can't be extracted easily.
Take a look at this code (you can paste it into the main() method of a LinqPad editor window to execute it):
var linq=from a in Attributes
let s = string.Join(",",a).TrimStart('{').TrimEnd('}').Split(',')
select new
{
Value = s[0].Split('=')[1].Trim(),
Name = s[1].Split('=')[1].Trim()
};
//linq.Dump();
Since you cannot access the Name and Value properties of the variable Attributes inside the object[] array because the compiler hides them from you you, the trick is here to use the Join(",", a) method to get around this limitation.
All you need to do afterwards is to trim and split the resulting string and finally create a new object with Value and Name properties.
You can try it out if you uncomment the linq.Dump(); line in LinqPad - it returns what you want and it is furthermore queryable by Linq statements.
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);
}
}
I have a list of String
List<String> lst=new List<String>{"A","B","C"}
And an xml file like
<Root>
<ChildList>
<Childs>
<Child Name="a1" Val="A"/>
<Child Name="a2" val="A"/>
<Child Name="b1" val="B"/>
</Childs>
</ChildList>
</Root>
i need to read contets of the xml file and add to a dictionary
Dictionary<String,List<String>> dict
where the dictionary key is the items in the "lst" and value is the attribute value of "Name" from the file
So the result will be like
Key(String) Value(List<String>)
"A" "a1","a2"
"B" "b1"
"C" null
now i'm using nested for loop for this
Is there any wau to do this using LINQ to XML
Thanks in advance
I think this will do it:
XDocument doc = XDocument.Load("foo.xml");
ILookup<string, string> lookup = doc.Descendants("Childs")
.First()
.Elements("Child")
.ToLookup(x => (string) x.Attribute("Val"),
x => (string) x.Attribute("Name"));
var dictionary = lst.ToDictionary(x => x,
x => lookup[x].ToList().NullIfEmpty());
Using a helper method:
public static List<T> NullIfEmpty<T>(this List<T> list)
{
return list.Count == 0 ? null : list;
}
If you don't mind having an empty list instead of null for items which aren't in the XML file, the second statement can be simplified, with no need for the helper method:
var dictionary = lst.ToDictionary(x => x, x => lookup[x].ToList());
Note that I've structured this so that it only needs to go through the XML file once, instead of searching through the file once for each element in the list.
var xml = #"<Root>
<ChildList>
<Childs>
<Child Name=""a1"" Val=""A""/>
<Child Name=""a2"" Val=""A""/>
<Child Name=""b1"" Val=""B""/>
</Childs>
</ChildList>
</Root>";
var lst= new List<String> { "A", "B", "C" };
var doc = XDocument.Parse(xml);
var dict = (from item in lst
select new
{
Key = item,
Value = (from elem in doc.Root.Element("ChildList").Element("Childs").Elements("Child")
where (string)elem.Attribute("Val") == item
select (string)elem.Attribute("Name")).ToList()
}).ToDictionary(i => i.Key, i => i.Value);
This can be made more efficient. I iterate over the elements once for every item in lst. I'll properly come up with another solution later if others don't come up with one.