Linq XML后代在枚举时丢失

时间:2012-07-07 00:14:58

标签: c# xml linq xml-parsing

我有一个像这样的XML文档

<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>

我正在尝试查找重复的项目,然后再次使用像此

这样的逻辑复制子项目
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);
}

这很有效,但是当我尝试使用duplicateItems时会出现问题。只要它枚举(比如在foreach duplicateItems中),第一个项目就会丢失它的子项的上下文。第二个就好了。

例如,稍后在代码中我说

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

我希望“allItemB”在第一遍中包含2,然后在第二遍中包含234。最终发生的事情是它只包含34,因为一旦枚举了duplicateItems数组,第一个XElement将丢失它的子项。

有谁知道如何解决这个问题?

1 个答案:

答案 0 :(得分:2)

如果我理解你的问题,你想要allItemB有3个元素 - allItemB [0]是值为2的XElement childB,[1]是3,[2]是4?

如果是这样,问题就在于您声明duplicateItems。你的代码甚至都没有编译,因为变量的范围仅限于第一个foreach循环,因此在第二个循环中不可用。

获取上述结果的代码:

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"));
 }

编辑:忘了提到我在第一个foreach循环中更改了OrderBy,因为示例xml文件没有CreatedOn属性。

如果你愿意,你可以使用更多的Linq并完全删除foreach循环,如下所示:

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") );