从XELEMENT var + LINQ到XML提取满足条件的所有元素

时间:2011-06-01 22:39:22

标签: lambda linq-to-xml

我有两个XELEMENTS XRXTemp

我的XR包含:

     <result xmlns="http://abc.com/test">
           <report>
            <unit unit_number="1" id="S1">
                 <classification>Subject</classification>
                 <claim_reported count="0" status="No claims reported" />
            </unit>
           <unit unit_number="2" id="S2">
                <classification>Subject</classification>
                <claim_reported count="0" status="No claims reported" />
           </unit>
        <unit unit_number="3" id="V1">
          <classification>Vehicle</classification>
          <claim_reported count="1" status="Claims reported" />
        </unit>
    ......
     </report>
</result>

我的XTemp包含:

 <result xmlns="http://abc.com/test">
   <report>
        <unit unit_number="1" id="S3">
             <classification>Subject</classification>
             <claim_reported count="1" status="Claims reported" />
        </unit>
       <unit unit_number="2" id="S4">
            <classification>Subject</classification>
            <claim_reported count="4" status="Claims reported" />
       </unit>
      <unit unit_number="3" id="V2">
         <classification>Vehicle</classification>
         <claim_reported count="0" status="No claims reported" />
      </unit>
      <unit unit_number="3" id="V3">
         <classification>Vehicle</classification>
         <claim_reported count="2" status="Claims reported" />
      </unit>
         ......
    </report>
 </result>

尝试根据某个条件从<unit>中提取所有XTemp个元素,并将其添加到XR

条件是:获取id为“V”的所有<unit>个元素。

下面的代码是我能想到的最好的代码。

但是,我得到Object reference not set to an instance错误。

XR.Descendants(xmlns + "unit").LastOrDefault().AddAfterSelf(XTemp.Descendants(xmlns + "unit")
  .Where(x => x.Element(xmlns + "unit").Attribute("id").Value.ToUpper().Contains("V")));

提前致谢

BB

1 个答案:

答案 0 :(得分:2)

你的问题出现在这段代码中:

.Where(x => x.Element(xmlns + "unit").Attribute("id")...

在查询中,您已经查询了unit个元素。 unit不包含嵌套unit,这就是您获得NullReferenceException的原因。

将其更改为:.Where(x => x.Attribute("id")...

就个人而言,我发现您的查询难以阅读。首先,LastOrDefault意味着你可能期望存在一个空值,但是你正在寻找其余的查询而不解决它。其次,您不需要找到最后一个unit元素和AddAfterSelf。相反,您可以直接Addreport元素,元素将附加到结尾:

var query = xtemp.Descendants(xmlns + "unit")
                 .Where(e => e.Attribute("id").Value.StartsWith("V", StringComparison.InvariantCultureIgnoreCase));

xr.Element(xmlns + "report").Add(query);

另外,请注意我在忽略案例时如何检查“V”。与使用ToUpper相反,这被认为是最佳做法。


编辑:根据评论,这里是一个解决方案,对所有unit节点进行排序,同时保留非unit节点的现有顺序。此代码考虑节点是在现有节点之前和之后,还是根本没有其他节点。

一些假设是:

  • XR始终有unit个节点
  • ID遵循现有的字母,后跟数字格式。
  • unit节点保持在一起,不会散布在所有其他类型的节点之间。

如果它们不正确,前两个假设是很难解决的问题。

var query = xtemp.Descendants(xmlns + "unit")
                 .Where(e => e.Attribute("id").Value.StartsWith("V", StringComparison.InvariantCultureIgnoreCase));

var units = xr.Element(xmlns + "report").Elements(xmlns + "unit");
var beforeUnit = units.First().ElementsBeforeSelf().FirstOrDefault();
var afterUnit = units.Last().ElementsAfterSelf().FirstOrDefault();

// order based on ID starting letter, then integer value
var orderedUnits = units.Concat(query)
                        .OrderBy(e => e.Attribute("id").Value[0])
                        .ThenBy(e => int.Parse(e.Attribute("id").Value.Substring(1)))
                        .ToList();

// remove original unit nodes
units.Remove();

// add ordered units based on their original position
if (beforeUnit != null)
{
    beforeUnit.AddAfterSelf(orderedUnits);
}
else if (afterUnit != null)
{
    afterUnit.AddBeforeSelf(orderedUnits);
}
else
{
    xr.Element(xmlns + "report").Add(orderedUnits);
}