简体   繁体   中英

Linq-to-xml group by elements that don't have child elements

I was wondering if anyone can help with the following:

I've got a xml document like this:

<div class="_cl">element description 1</div>
<div class="_cl">
    <anotherele>dtls</anotherele>
    <anotherele>moredtls</anotherele>
</div>
<div class="_cl">
    <anotherele>dtls</anotherele>
    <anotherele>moredtls</anotherele>
</div>
<div class="_cl">element description 2</div>
<div class="_cl">
    <anotherele>dtls</anotherele>
    <anotherele>moredtls</anotherele>
</div>
<div class="_cl">
    <anotherele>dtls</anotherele>
    <anotherele>moredtls</anotherele>
</div>

I was wondering if there was any way in linq to group this by elements that don't have child elements. Basically, trying to structure the document somehow like this:

<div class="_cl">element description 1
   <div class="_cl">
       <anotherele>dtls</anotherele>
       <anotherele>moredtls</anotherele>
   </div>
   <div class="_cl">
       <anotherele>dtls</anotherele>
       <anotherele>moredtls</anotherele>
   </div>
</div>
<div class="_cl">element description 2
   <div class="_cl">
       <anotherele>dtls</anotherele>
       <anotherele>moredtls</anotherele>
   </div>
   <div class="_cl">
       <anotherele>dtls</anotherele>
       <anotherele>moredtls</anotherele>
   </div>
</div>

My first woeful attempt looks like this:

var n = from a in doc.Descendants()
                where a.Name.LocalName == "div" && (string)a.Attribute("class") == "_cl"
                group a by a.Value.Length<50 into g 
                select new { k = g.Key, p = g.Count() };

Hope this makes sense and thanks in advance.

Stu

Here is an example, assuming the input is

<body>
<div class="_cl">element description 1</div>
<div class="_cl">
  <anotherele>dtls</anotherele>
  <anotherele>moredtls</anotherele>
</div>
<div class="_cl">
  <anotherele>dtls</anotherele>
  <anotherele>moredtls</anotherele>
</div>
<div class="_cl">element description 2</div>
<div class="_cl">
  <anotherele>dtls</anotherele>
  <anotherele>moredtls</anotherele>
</div>
<div class="_cl">
  <anotherele>dtls</anotherele>
  <anotherele>moredtls</anotherele>
</div>
</body>

then the code

    XDocument input = XDocument.Load("input.xml");

    XDocument output = new XDocument(
        new XElement(input.Root.Name,
            from el in input.Root.Elements()
            where el.Elements().Any()
            group el by el.NodesBeforeSelf().OfType<XElement>().LastOrDefault(e => !e.Elements().Any()) into g
            select new XElement(g.Key.Name,
                g.Key.Attributes(),
                g.Key.Nodes(),
                g)
            ));

    output.Save(Console.Out);

outputs

<body>
  <div class="_cl">element description 1<div class="_cl"><anotherele>dtls</anoth
erele><anotherele>moredtls</anotherele></div><div class="_cl"><anotherele>dtls</anotherele><anotherele>moredtls</anotherele></div></div>
  <div class="_cl">element description 2<div class="_cl"><anotherele>dtls</anoth
erele><anotherele>moredtls</anotherele></div><div class="_cl"><anotherele>dtls</anotherele><anotherele>moredtls</anotherele></div></div>
</body>

This is what you want I think, with the exception of the indentation. As for the indentation, you might get closer to your desired result by loading the input with XDocument.Load("input.xml", LoadOptions.PreserveWhitespace) .

For more on this grouping approach, see http://msmvps.com/blogs/martin_honnen/archive/2009/11/27/grouping-with-linq-to-xml.aspx .

Svarog is right; what you're trying to do can't be achieved by grouping. You'd need some more advanced Linq expressions than are given in the Linq keywords.

Try this:

var n = doc.Elements().Where(div=>!div.Elements().Any()).
        Select(div=>{
            div.Add(
                div.ElementsAfterSelf().TakeWhile(x=>x.Elements().Any())
            ); 
            return div;
        }); 

Thanks for your input, but none of the solutions seem to quite cut it. I think what I'll do is just loop through the elements and parse it manually. I just thought a nice linq one-liner would be better.

Cheers

Stu

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM