从XELEMENT var + LINQ中提取符合条件的所有元素到XML

问题描述:

我有两个XELEMENTS XRXTemp从XELEMENT var + LINQ中提取符合条件的所有元素到XML

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

试图基于某种condtion从XTemp提取所有<unit>元素,并将它们添加到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

+0

您是否介意包含您正在使用的XML片段以及导致该行的其他代码?不要让我们更难了解你的情况,给我们提供我们需要的信息,不要指望我们必须解决它。 – 2011-06-02 01:21:45

你的问题是这段代码:

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

在查询这一点你已经在查询元素unit。 A 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节点
  • 标识遵循现有的字母后面的数字格式。
  • 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); 
} 
+0

@Ahmad非常感谢。感谢您提出宝贵的建议。我希望所有的ID以“S”开头的元素出现,然后是我的XR中以“V”开头的元素。我怎样才能做到这一点? – BumbleBee 2011-06-02 18:21:59

+0

除了元素,我的也可能包含其他元素,在这种情况下,我想仅在XR中最后一次发生元素之后才添加提取的元素。 – BumbleBee 2011-06-02 18:33:04

+0

@BumbleBee排序是可能的,但你的第二个评论使问题变得复杂一点。如果其他元素可以在''下存在,他们是否需要按字母顺序排序?或者应该保留现有订单,而只有''元素被排序?前者更容易;后者需要一些额外的咖啡因:) – 2011-06-02 18:37:00