Linq XML descendants being lost on enumeration

六月ゝ 毕业季﹏ 提交于 2019-12-25 00:57:28

问题


I have an XML document like this

<root>
  <item id="1" creator="me">
    <childA>1</childA>
    <childB>2</childB>
  </item>
  <item id="2" creator="me">
    <childA>1</childA>
    <childB>3</childB>
    <childB>4</childB>
  </item>
</root>

I'm trying to find duplicate items, then again duplicate child items for the duplicate items with logic like this

XDocument XmlRoot //whatever...you get the point

// Get item nodes
var items = XmlRoot.Descendants("item");

// Find duplicate items keys using creator attribute
var duplicateItemKeys = items.GroupBy(x => x.Attribute("creator").Value)
.Where(g => g.Count() > 1)
.Select(g => g.Key);

foreach(var duplicateItemKey in duplicateItemKeys)
{
  // Get the duplicate item XML elements using the duplicate keys
  var duplicateItems = items.Where(x => x.Attribute("creator").Value == duplicateToucheKey)
      .OrderBy(xelement => xelement.Attribute("CreatedOn").Value);
}

This works, however there is a problem later when I try to use duplicateItems. Any time it enumerates (like in a foreach duplicateItems) the first item looses the context of it's children. The second one is just fine.

So for example, later in code I say

var allItemB = new List<XElement>();
foreach (duplicateItem in duplicateItems) 
{
  allItemB.AddRange(duplicateItem.Descendants("childB"));
}

I expect "allItemB" to contain 2 on the first pass, then 234 on the second. What ends up happening is that it only contains 34 because once the duplicateItems array is enumerated the first XElement looses it's children.

Does anyone know how to fix this?


回答1:


If I understand your question correctly, you want allItemB to have 3 elements - allItemB[0] is the XElement childB with value 2, [1] to be 3 and [2] to be 4?

If so, the problem is where you are declaring duplicateItems. Your code same doesn't even compile, as the variable's scope is limited to the first foreach loop, and thus not available at the second.

My code to get the above result:

XDocument XmlRoot = XDocument.Load( "C:\\somefile.xml" );

// Get item nodes
var items = XmlRoot.Descendants("item");

// Find duplicate items keys using creator attribute
var duplicateItemKeys = items.GroupBy(x => x.Attribute("creator").Value)
     .Where(g => g.Count() > 1)
     .Select(g => g.Key);

IEnumerable<XElement> duplicateItems = new List<XElement>();
foreach(var duplicateItemKey in duplicateItemKeys)
{
     // Get the duplicate item XML elements using the duplicate keys
     duplicateItems = items.Where(x => x.Attribute("creator").Value == duplicateItemKey)
          .OrderBy(xelement => xelement.Attribute("id").Value);
 }

 var allItemB = new List<XElement>();
 foreach (var duplicateItem in duplicateItems) 
 {
      allItemB.AddRange(duplicateItem.Descendants("childB"));
 }

Edit: forgot to mention that I changed the OrderBy in the first foreach loop because the sample xml file didn't have the CreatedOn attribute.

And if you want, you can use a little more Linq and drop the foreach loops entirely, like so:

XDocument XmlRoot = XDocument.Load( "C:\\somefile.xml" );

// Get item nodes
var items = XmlRoot.Descendants("item");

// Find duplicate items keys using creator attribute
var duplicateItemKeys = items.GroupBy(x => x.Attribute("creator").Value)
     .Where(g => g.Count() > 1)
     .Select(g => g.Key);

// Get the duplicate item XML elements using the duplicate keys
var duplicateItems = items.Where(i => duplicateItemKeys.Contains(i.Attribute("creator").Value))
     .OrderBy( xelement => xelement.Attribute("id").Value );

// Get the child nodes named childB
var allItemB = new List<XElement>();
allItemB.AddRange( duplicateItems.Descendants("childB") );


来源:https://stackoverflow.com/questions/11371200/linq-xml-descendants-being-lost-on-enumeration

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!