Lets say I have this xml:
<categories>
<category text="Arts">
<category text="Design"/>
<category text="Visual Arts"/>
</category>
<category text="Business">
<category text="Business News"/>
<category text="Careers"/>
<category text="Investing"/>
</category>
<category text="Comedy"/>
</categories>
I want to write a LINQ query that will return the category and it's parent category if it has any.
For example, if I was searching for "Business News" I would want it to return an XElement containing the following:
<category text="Business">
<category text="Business News" />
</category>
If I only search for "Business", I would just want
<category text="Business" />
So far the best I can do is use LINQ to get the element I'm searching for, then check if the parent of the node I found is the root node and adjust accordingly. Is there a better way?
It is also possible for a node to have only one child, as is the case with the unary minus operator. An expression tree, T, can be evaluated by applying the operator at the root to the values obtained by recursively evaluating the left and right subtrees.
Yes it can. You just need to make sure that for all vertices c in the left subtree of p it must hold that val(c)\leq val(p), and similarily for the right subtree val(c)\geq val(p).
A node with no children is a leaf node; a node with children is a parent node.
The easy part is to get the path to the element:
IEnumerable<XElement> elementsInPath =
doc.Element("categories")
.Descendants()
.Where(p => p.Attribute("text").Value == "Design")
.AncestorsAndSelf()
.InDocumentOrder()
.ToList();
The InDocumentOrder() is there to get the collection in the order of root, child, grandchild. The ToList() is there to avoid any unwanted effects in the next step.
Now, the less beautiful part, which maybe could be done in a more elegant way:
var newdoc = new XDocument();
XContainer elem = newdoc;
foreach (var el in elementsInPath))
{
el.RemoveNodes();
elem.Add(el);
elem = elem.Elements().First();
}
That's it. Since each XElement keeps their child, we have to remove the children from each node in the path, and then we rebuild the tree.
Given the input, and the requirements as stated, this will do what you want:
public static class MyExtensions
{
public static string ParentAndSelf(this XElement self, XElement parent)
{
self.Elements().Remove();
if (parent != null && parent.Name.Equals(self.Name))
{
parent.Elements().Remove();
parent.Add(self);
return parent.ToString();
}
else
return self.ToString();
}
}
class Program
{
[STAThread]
static void Main()
{
string xml =
@"<categories>
<category text=""Arts"">
<category text=""Design""/>
<category text=""Visual Arts""/>
</category>
<category text=""Business"">
<category text=""Business News""/>
<category text=""Careers""/>
<category text=""Investing""/>
</category>
<category text=""Comedy""/>
</categories>";
XElement doc = XElement.Parse(xml);
PrintMatch(doc, "Business News");
PrintMatch(doc, "Business");
}
static void PrintMatch(XElement doc, string searchTerm)
{
var hit = (from category in doc
.DescendantsAndSelf("category")
where category.Attributes("text")
.FirstOrDefault()
.Value.Equals(searchTerm)
let parent = category.Parent
select category.ParentAndSelf(parent)).SingleOrDefault();
Console.WriteLine(hit);
Console.WriteLine();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With