How to get the parent and only one child of a node

Suppose 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 a category and its parent category, if any.

For example, if I were looking for "Business News," I would like it to return an XElement containing the following:

<category text="Business">
   <category text="Business News" />
</category>

If I'm just looking for "Business," I just want

<category text="Business" />

So far, the best I can do is use LINQ to get the item I'm looking for, and then check if the parent node node root node is found and adjusted accordingly. Is there a better way?

+5
source share
5 answers

The simple 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();

InDocumentOrder() , , . ToList() , .

, , :

var newdoc = new XDocument();
XContainer elem = newdoc;
foreach (var el in elementsInPath))
{
    el.RemoveNodes();
    elem.Add(el);
    elem = elem.Elements().First();
}

. XElement , node , .

+3

, :

public static IEnumerable<XElement> FindElements(XElement d, string test)
{
    foreach (XElement e in d.Descendants()
        .Where(p => p.Attribute("text").Value == test))
    {
        yield return e;
        if (e.Parent != null)
        {
            yield return e.Parent;
        }
    }
}

, Linq, :

List<XElement> elms = FindElement(d, "Visual Arts").ToList();

foreach (XElement elm in FindElements(d, "Visual Arts"))
{
   ...
}

Edit:

, , , , . , , , , XElement , , - .

, - . , :

XElement result = doc.Descendants()
                     .Where(x => x.Attribute("text").Value == test)
                     .Select(
                         x => x.Parent != null && x.Parent.Attribute("text") != null
                                ? new XElement(
                                        x.Parent.Name,
                                        new XAttribute("text", x.Parent.Attribute("text").Value),
                                        new XElement(
                                            x.Name, 
                                            new XAttribute("text", x.Attribute("text").Value)))
                                : new XElement(
                                    x.Name, 
                                    new XAttribute("text", x.Attribute("text").Value)))
                    .FirstOrDefault();
+1

, , , :

    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();
        }
    }
+1

, :

XDocument xmlFile;

return from c in xmlFile.Descendants("category")
       where c.Attribute("text").Value == "Business News"
       select c.Parent ?? c;

?? XElement, null 'c'.

. , , , , :

var cat = from c in doc.Descendants("category")
          where c.Attribute("text").Value == "Business News"
          let node = c.Parent ?? c
          select c.Parent == null
                     ? c // Parent null, just return child
                     : new XElement(
                           "category",
                           c.Parent.Attributes(), // Copy the attributes
                           c                      // Add single child
                           );
0
var text = "Car";

var el = from category in x.Descendants("category")
         from attribute in category.Attributes("text")
         where attribute.Value.StartsWith(text)
         select attribute.Parent.Parent;


Console.WriteLine(el.FirstOrDefault());

:

<category text="Business">...

, .

0

All Articles