如果我理解您的问题正确,您只想获取具有属性的最里面的元素ResultCode="ERROR"
。因为您的示例数据<PRODUCT>
不应该出现在结果中,因为它有一个带有ResultCode="ERROR"
.
该Descendants
方法返回文档中的所有后代元素。因此,仅按属性过滤将包括<PRODUCT>
节点。所以你需要为每个元素的子元素添加一个检查:
var doc = XDocument.Load("Test.xml");
var errors = doc.Descendants()
.Where(e => e.Attribute("ResultCode") != null &&
e.Attribute("ResultCode").Value == "ERROR" &&
!e.Elements().Any(c => c.Attribute("ResultCode") != null &&
c.Attribute("ResultCode").Value == "ERROR"));
这将只返回那些没有任何错误子元素的元素。
ResultCode
请注意,如果您的 XML 中的每个节点都有一个属性,则可以省略对 null 的检查。如果不是,您将获得一个NullReferenceException
. 我建议为该检查编写一个小辅助方法:
public static bool HasError(XElement element)
{
var resultCode = element.Attribute("ResultCode");
return resultCode != null && resultCode.Value == "ERROR";
}
var errors = doc.Descendants()
.Where(e => HasError(e) && !e.Elements().Any(c => HasError(c)));
另请注意,这将返回ResultCode="ERROR"
即使其父级没有错误的元素。
如果您的要求是只有在其父元素也有错误时才应将元素包含在结果中,并且如果您不确定 XML 是否总是这样形成,您将需要编写一个递归函数:
public static IEnumerable<XElement> InnermostErrors(XElement root)
{
var resultCode = root.Attribute("ResultCode");
if (resultCode == null || resultCode.Value != "ERROR")
{
yield break;
}
var childrenWithError = root.Elements().Where(e => HasError(e));
if (!childrenWithError.Any())
{
yield return root;
}
foreach (var inner in childrenWithError.SelectMany(e => InnermostErrors(e)))
{
yield return inner;
}
}