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
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;
I am trying to write a Linq query which will parse my XML tree(which is actually a SQL deconstructed into an XML tree).
My XML looks like this
<SqlRoot>
<SqlStatement>
<Clause>
<OtherKeyword>select</OtherKeyword>
<WhiteSpace></WhiteSpace>
<Other>t1</Other>
<Period>.</Period>
<Other>empid</Other>
<WhiteSpace></WhiteSpace>
</Clause>
<Clause>
<OtherKeyword>from</OtherKeyword>
<SelectionTarget>
<WhiteSpace></WhiteSpace>
<Other>bd_orm</Other>
<Period>.</Period>
<Other>dbo</Other>
<Period>.</Period>
<Other>bal_impacts_t</Other>
<WhiteSpace></WhiteSpace>
<Other>t1</Other>
</SelectionTarget>
</Clause>
</SqlStatement>
</SqlRoot>
I am trying to pick out the table name (SelectionTarget node). The WhiteSpace node is a blank/white space in between the values.
So the output which I expect is something like this bd_orm.dbo.bal_impacts_t t1 but I am unable to figure out how to do this by including a whitespace in between.
I tried this
var xxx = (from res in xDoc.Descendants("Clause").Descendants("SelectionTarget") select res);
Console.WriteLine(xxx.DescendantNodesAndSelf().OfType<XElement>().First().Value);
but it obviously fail cause I do not know how to take into account the whitespace node and convert that into an actual whitespace. Any suggestions?
Simply select a space for the WhiteSpace nodes and the string value for all other nodes, then concatenate the results:
var parts = doc.Descendants("SelectionTarget")
.Elements()
.Select(e => e.Name == "WhiteSpace" ? " " : (string)e);
var text = string.Concat(parts);
var nodes = (from res in xDoc.Descendants("Clause")
.Descendants("SelectionTarget")
.Descendants()
select res);
string name = String.Join("", nodes.Select(n=>n.Name == "WhiteSpace"?" ":n.Value));
name: bd_orm.dbo.bal_impacts_t t1
demo
nodes:
<WhiteSpace></WhiteSpace>
<Other>bd_orm</Other>
<Period>.</Period>
<Other>dbo</Other>
<Period>.</Period>
<Other>bal_impacts_t</Other>
<WhiteSpace></WhiteSpace>
<Other>t1</Other>
You could add the whitespace before constructing the query:
foreach (var ws in xDoc.Descendants("WhiteSpace"))
{
ws.Value = " ";
}
I have an XML file that looks like this -
<SST_SignageCompConfig>
<Items>
<Item>
<Index>0</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Branding-Colours-for-business.jpg</Name>
</Item>
<Item>
<Index>1</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Flower of Life Meditation - Copy.png</Name>
</Item>
</Items>
</SST_SignageCompConfig>
I need to count how many Item Elements there are within the Items Element.
ie how many images there are.
I'm using XDocument, so my XML file is loaded like this -
string configurationPath = System.IO.Path.Combine("C:\\SST Software\\DSS\\Compilations\\" + compName + #"\\Comp.cfg");
XDocument filedoc = XDocument.Load(configurationPath);
I've tried numerous variations of the following, with all returning a null object reference exception
foreach (var item in filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Nodes())
{
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Attribute("Name").ToString();
files.Append(name + "|");
}
I've found countless examples of how to count how many different child elements are within an element, but I need to know how many instances of the same element exist.
Can anyone point me in the right direction?
You can select all names like so:
var names = from item in filedoc.Descendants("Item")
select (string)item.Element("Name");
Or without the query syntax:
var names = filedoc.Descendants("Item").Elements("Name").Select(e => e.Value);
You can get only unique names by:
var uniqueNames = names.Distinct();
You're on the right track. Try finding out exactly which invocation is giving you the NullReferenceException. My guess is that it's the attempt to find:
.Element("SST_SignageCompConfig")
Which is your root. Try the following instead:
// note the difference between .Element and .Elements
var count = filedoc.Root.Element("Items").Elements("Item").Count();
You could also use XPath to help you nail down the navigation within your XDocument:
// returns the current top level element
var element = filedoc.Root.XPathSelectElement(".");
// If the returned element is "SST_SignageCompConfig", then:
var nextElement = filedoc.Root.XPathSelectElement("./Items")
// If the "." element is *not* "SST_SignageCompConfig", then try and locate where in your XML document that node is.
// You can navigate up with .Parent and down with .Element(s)
And so on.
How about:
var nav = fileDoc.CreateNavigator();
XPathNodeIterator navShape = nav.Select("/SST_SignageCompConfig/Items");
navShape.MoveNext()
var count = navShape.Count;
If your xml has only one Items element, this should do the trick:
filedoc.Descendants("Item")
.GroupBy(e => e.Element("Name")!=null? e.Element("Name").Value:String.Empty)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
});
Because "Name" is an element and not an attribute of your xml structure.
can you try replacing this?
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Element("Name").ToString();
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 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.