I have this XML
<?xml version="1.0" encoding="UTF-8" ?>
<uclassify xmlns="http://api.uclassify.com/1/ResponseSchema" version="1.01">
<status success="true" statusCode="2000"/>
<readCalls>
<classify id="cls1">
<classification textCoverage="1">
<class className="female" p="0.932408"/>
<class className="male" p="0.0675915"/>
</classification>
</classify>
</readCalls>
</uclassify>
or similar. What matters is, I don't have
<tag>value</tag>
but
<tag attribute1 attribute2 ... />.
What I want to output is for instance
attribute1: attributevalue1
So I want to enter a term like "female" and I want it to output 0.932408.
What I tried to get started
string xml = HttpGet("http://uclassify.com/browse/" + username + "/" + classifiername + "/" + operation + "?" + paramz.ToString());
XDocument doc = XDocument.Parse(xml);
var list = doc.Root.Elements("uclassify")
.Select(element => element.Value)
.ToList();
But list is always empty, which is presumably because there are no values, only attributes.
EDIT:
current version
string xml = HttpGet("http://uclassify.com/browse/" + username + "/" + classifiername + "/" + operation + "?" + paramz.ToString());
XDocument doc = XDocument.Parse(xml);
XNamespace ns = "http://api.uclassify.com/1/ResponseSchema";
var list = doc.Root.Descendants(ns + "class")
.Select(element => element.Value)
.ToList();
textBox1.Text = string.Join(",", list.ToArray());
Result is a comma.
SO your problem is the default namespace:
xmlns="http://api.uclassify.com/1/ResponseSchema"
To fix it, you need to qualify your element selector.
You can do this like so...
XNamespace ns = "http://api.uclassify.com/1/ResponseSchema";
var list = doc.Root.Descendants(ns + "class")
.Select(element => element.Value)
.ToList();
I've modified your code slightly to select all the class nodes, but you can see that I've prefaced your "class" in the Descendants() call with the namespace variable ns.
EDIT:
So now your problem is that you are selecting the element's values, not the attributes values...
so if we are building a dictionary of attribute names to attribute values, you might want to use some code like this:
Dictionary<string,double> dictionary = doc.Root.Descendants(ns + "class")
.ToDictionary(
element => element.Attribute("className").Value,
element => double.Parse(element.Attribute("p").Value));
foreach(var item in dictionary)
{
Console.WriteLine(string.Format("{0}: {1}", item.Key, item.Value));
}
so a couple of caveats:
I'm assuming that each of the className attributes in the class attributes are unique, otherwise you'll have an exception
I'm assuming the value of the attribute p is a double.
Related
I have an XML document which basically looks like this:
<Item>
<Seller>
<UserID>SomeSeller</UserID>
<FeedbackScore>2535</FeedbackScore>
</Seller>
</Item>
Now I'm trying to parse the document like following:
var document = XDocument.Parse(upcList);
XNamespace ns = "urn:ebay:apis:eBLBaseComponents";
var result= document
.Descendants(ns + "Item")
.Select(item => new CustomClass
{
FeedBackScore = Convert.ToInt32(item.Descendants(ns+ "Seller")
.Where(p=>p.Name.LocalName=="FeedbackScore").FirstOrDefault()),
Sales = (int) item.Element(ns+"QuantitySold"),
Location = (string)item.Element(ns+"Location"),
CurrentPrice = (double)item.Element(ns + "CurrentPrice"),
Title = (string)item.Element(ns + "Title"),
ItemID = (string)item.Element(ns + "ItemID")
}).ToList();
Please note this part how I try to parse the FeedbackScore node value:
FeedBackScore = Convert.ToInt32(item.Descendants(ns+ "Seller")
.Where(p=>p.Name.LocalName=="FeedbackScore").FirstOrDefault()),
But when I try to parse it I'm getting all "FeedbackScore" nodes values as "0" :(
Can someone tell me what am I doing wrong and how can I fetch this value inside this node "2535"?
FeedBackScore = Convert.ToInt32(item.Descendants(ns + "FeedbackScore").Value)
You have mistakenly checked the names of the Seller Nodes not its Children. By doing so, the FirstOrDefault() will yield null(condition of Where() is never met due the wrong Node) and Convert.ToIn32(null) will yield 0.
To fix this, you can go for the "FeedbackScore" Node directly and Convert its Value like this
FeedBackScore = Convert.ToInt32(item.Descendants("FeedBackValue").FirstOrDefault()?.Value),
Descendants here will return Seller elements, and then you check if any of them have the name FeedbackScore. This isn't possible - they can't have two names at once.
Assuming you want the FeedbackScore only if the parent is Seller, you need to read the Elements of the Seller elements.
I'd also note you can use the explicit conversions in the same way you do for other properties.
Putting that together, this would work:
FeedBackScore = (int) item.Elements(ns + "Seller")
.Elements(ns + "FeedbackScore")
.Single()
If this element isn't always present, you can default to 0:
FeedBackScore = (int?) item.Elements(ns + "Seller")
.Elements(ns + "FeedbackScore")
.SingleOrDefault() ?? 0;
enter code herein the XML document :
<foo>
<bar><para> test </para> </bar>
<bar>text</bar>
<bar>stackoverflow</bar>
</foo>
I am trying to parse it and only get the Strings in bar; by using this way:
[function where node is the foo]
foreach (XmlNode subNode in node.ChildNodes)
{
if (subNode.Name == "bar")
{
if (!String.IsNullOrWhiteSpace(subNode.InnerText))
Debug.WriteLine(subNode.Name + " - " subNode.InnerText);
}
}
However it gives me test
Thanks
This is what you are looking for (EDITTED based on you updated question)
XDocument doc = XDocument.Load(path to your xml file);
XNamespace ns = "http://docbook.org/ns/docbook";
var result = doc.Root.Descendants(ns + "para")
.Where(x => x.FirstNode.NodeType == System.Xml.XmlNodeType.Text)
.Select(x => x.Value)
.ToList();
In your updated xml I see you are using a namespace so the name of your node is not para but it's theNameSpace + "para". The namespace is defined in the first line of your xml file. Also you can see this sample too.
Do you want "test"? Try following :
string input =
"<foo>" +
"<bar><para> test </para> </bar>" +
"<bar>text</bar>" +
"<bar>stackoverflow</bar>" +
"</foo>";
XDocument doc = XDocument.Parse(input);
List<string> bars = doc.Descendants("bar").Where(x => x.NextNode != null).Select(x => (string)((XElement)x.NextNode)).ToList();
I have some XML that looks like this:
<root>
<instructions type="array">
<item type="string">Cannot do business directly with consumers</item>
<item type="string">Cannot marry a martian</item>
</instructions>
</root>
and an instructions variable that looks like this:
var instructions = myXDocument.XPathSelectElements("/root/instructions").Nodes();
I am attempting to concatenate all of the item strings, thusly:
"Cannot do business directly with consumers, Cannot marry a martian"
and my attempt so far is
instructions.Select(x => x.[[What do I put here?]]).Aggregate((i,j) => i + ", " + j)
but having trouble figuring out how to get the inner text from each node in my lambda expression. x.ToString() yields "<item type="string">Cannot do business directly with consumers</item>"
Using the same approach, you can simply replace Nodes() with Elements(), and then access the Value property of the returned XElements to get the inner texts :
var instructions = myXDocument.XPathSelectElements("/root/instructions").Elements();
var result = instructions.Select(x => x.Value).Aggregate((i,j) => i + ", " + j);
I know this is not the lambda expression which you are looking for but this is way around to get the resultant output.
string result;
XElement root = XElement.Load(SomeXML);
root.Element("root").Elements("instructions").Elements("item").All<XElement>(xe =>
{
result = result + xe.Attribute("type").Value),
return true;
});
Try this
var xml = "<root><instructions type=\"array\"><item type=\"string\">Cannot do business directly with consumers</item><item type=\"string\">Cannot marry a martian</item></instructions></root>";
var document = XDocument.Parse(xml);
var result = string.Join(", ", document.Descendants("instructions").Elements("item").Select(x=>x.Value));
Output:
Cannot do business directly with consumers, Cannot marry a martian
I have an XML document that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<searchlayers>
<searchlayer whereClause="ProjectNumber=a">Herbicide</searchlayer>
<searchlayer whereClause="ProjectNumber=b">Herbicide - Point</searchlayer>
<searchlayer whereClause="ProjectNumber=c">miscellaneous</searchlayer>
<searchlayer whereClause="ProjectNumber=d">miscellaneous - Point</searchlayer>
<searchlayer whereClause="ProjectNumber=e">Regrowth Control</searchlayer>
<searchlayer whereClause="ProjectNumber=f">Regrowth Control - Point</searchlayer>
<searchlayer whereClause="ProjectNumber=g">Tree Removal</searchlayer>
<searchlayer whereClause="ProjectNumber=h">Tree Removal - Point</searchlayer>
<searchlayer whereClause="ProjectNumber=i">Trimming</searchlayer>
<searchlayer whereClause="ProjectNumber=j">Trimming - Point</searchlayer>
</searchlayers>
</configuration>
Is it possible to write one single Linq statement to get each of the element (e.g. Herbicide, miscellaneous, Regrowth Control... etc) with its matching whereClause (e.g. for Herbicide, the where clause would be "ProjectNumber=a")?
I can write two statements separately, one to get the elements, one to get the attributes, but it would be nice to write just one Linq statement that gets both at the same time.
Thanks.
Yes it is possible. But there are many possible data structure can be used to store list of 2 values pair, here is one example using Tuple :
XDocument doc = XDocument.Load("path_to_xml_file.xml");
List<Tuple<string, string>> result =
doc.Root
.Descendants("searchlayer")
.Select(o => Tuple.Create((string) o, (string) o.Attribute("whereClause")))
.ToList();
You can create a set of anonymous objects as follows:
var result = root.Element("searchlayers")
.Elements("searchlayer")
.Select(i =>
new {attribute = i.Attribute("whereClause").Value,
value = i.Value});
This will give a set of records, where the attributes are paired with the element values.
If you want this in query syntax, it looks like this:
var result = from el in root.Elements("searchlayers").Elements("searchlayer")
select new {attribute = el.Attribute("whereClause").Value,
value = el.Value};
You can use this to to select all elements matching Herbicide whose whereClause matches ProjectNumber=a
IEnumerable<XElement> result =
from el in doc.Elements("Herbicide")
where (string)el.Attribute("whereClause") == "ProjectNumber=a"
select el;
Another alternative would be:
var result = doc.Descendants()
.Where(e => e.Attribute("ProjectNumber=a") != null)
.ToList();
Which should provide you every element whose whereClause equals "ProjectNumber=a".
You can use the Attributes property to get the attribute from the XML node, along with the InnerText, like so:
XmlDocument doc = new XmlDocument();
doc.LoadXml(yourxml);
XmlNodeList xlist = doc.GetElementsByTagName("searchlayer");
for(int i=0;i<xlist.Count;i++)
{
Console.WriteLine(xlist[i].InnerText + " " + xlist[i].Attributes["whereClause"].Value);
}
If you must use LINQ, you could use XDocument and then return anonymous class objects consisting of the attribute and text, like so:
XDocument xdoc = XDocument.Load(yourxmlfile);
var result = xdoc.Root.Elements("searchlayers").Elements("searchlayers").Select(x => new {attr = x.Attribute("whereClause").Value, txt = x.Value});
foreach (var r in result)
{
Console.WriteLine(r.attr + " " + r.txt);
}
I have an XmlDocument object and xml in the format:
<?xml version="1.0" encoding="utf-8"?>
<S xmlns="http://server.com/DAAPI">
<TIMESTAMP>2010-08-16 17:25:45.633</TIMESTAMP>
<MY_GROUP>
<GROUP>1 </GROUP>
<NAME>Amsterdam</NAME>
....
</MY_GROUP>
<MY_GROUP>
<GROUP>2 </GROUP>
<NAME>Ireland</NAME>
....
</MY_GROUP>
<MY_GROUP>
<GROUP>3 </GROUP>
<NAME>UK</NAME>
....
</MY_GROUP>
Using a Lambda expression (or Linq To XML if it's more appropriate) on the XmlDocument object how can i do the following:
get the text of a specific element, say the text of NAME where GROUP = 1
the value of the first occurance of the element "NAME"
Thanks a lot
Assuming you mean XDocument rather than XmlDcoument:
First question:
XNamespace ns = "http://server.com/DAAPI";
string text = (from my_group in doc.Elements(ns + "MY_GROUP")
where (int) my_group.Element(ns + "GROUP") == 1
select (string) my_group.Element(ns + "NAME")).First();
I didn't really understand the second question... what do yuo mean by "contains an element of that name"? Which name? And if you're checking for NAME being equal to a give name, wouldn't you already know that name? Did you perhaps mean the value of GROUP for a specific name? If so, it's easy:
XNamespace ns = "http://server.com/DAAPI";
int group = (from my_group in doc.Elements(ns + "MY_GROUP")
where (string) my_group.Element(ns + "NAME")
select (int) my_group.Element(ns + "GROUP")).First();
Both of these queries assume that the values do exist, and that each MY_GROUP element has a GROUP and NAME subelement. Please let us know if that's not the case.
I have used Linq to XML.
string input = "<?xml version=\"1.0\" encoding=\"utf-8\"?><S xmlns=\"http://server.com/DAAPI\"><TIMESTAMP>2010-08-16 17:25:45.633</TIMESTAMP><MY_GROUP><GROUP>1 </GROUP><NAME>Amsterdam</NAME>....</MY_GROUP><MY_GROUP><GROUP>2 </GROUP><NAME>Ireland</NAME>....</MY_GROUP><MY_GROUP><GROUP>3 </GROUP><NAME>UK</NAME>....</MY_GROUP></S>";
var doc = XDocument.Parse(input);
XNamespace ns = "http://server.com/DAAPI";
//The first question
var name = (from elem in doc.Root.Elements(ns + "MY_GROUP")
where elem.Element(ns + "GROUP") != null //Checks whether the element actually exists - if you KNOW it does then it can be removed
&& (int)elem.Element(ns + "GROUP") == 1 //This could fail if not an integer - insure it is if nessasary
select (string)elem.Element(ns + "NAME")).SingleOrDefault();
I understood only your first question. Here you are for the first:
var xmlSource = myGroup.Load(#"../../MyGroup.xml");
var q = from c in xmlSource.myGroup
where c.group = 1
select c.name;