通过可能丢失的子节点在LINQ中排序XML
本文关键字:LINQ 排序 XML 子节点 | 更新日期: 2023-09-27 18:17:54
我正在处理一个结构如下的XML文件:
<vco:ItemDetail>
<cac:Item>
...
<cac:RecommendedRetailPrice>
<cbc:PriceAmount amountCurrencyID="EUR">4.95</cbc:PriceAmount>
<cbc:BaseQuantity quantityUnitCode="EA">1</cbc:BaseQuantity>
</cac:RecommendedRetailPrice>
...
</cac:Item>
...
</vco:ItemDetail>
RecommendedRetailPrice
和/或PriceAmount
可能不存在于每个Item
。尽管如此,我还是想按RecommendedRetailPrices
对所有Items
进行排序。所以我最终得到了下面的代码,它工作得很好,但必须在一些可怕的嵌套if语句的每个阶段检查null:
//full_xml is of type XDocument
var most_expensive = full_xml
.Element("ItemDetails")
.Elements(vco + "ItemDetail")
.Elements(cac + "Item")
.OrderBy(el =>
{
var rrp = el.Element(cac + "RecommendedRetailPrice");
string val = null;
if(rrp != null) {
var pa = rrp.Element(cbc + "PriceAmount");
if(pa != null) {
val = rrp.Value;
}
}
if (val != null) {
return Convert.ToDouble(val);
}
else {
return 0.0;
}
}
)
.Last();
在这种情况下,这可能不是那么糟糕,但我想一定有一种更习惯的方法来做到这一点。考虑按向下8级的可选子节点排序。
感谢您的帮助。
如果零或一个元素可以存在,那么我会使用Elements()
而不是Element()
,然后选择FirstOrDefault
。然后使用XElement
的显式强制转换操作符来获得您的值。
像这样
var most_expensive = full_xml
.Element("ItemDetails")
.Elements(vco + "ItemDetail")
.Elements(cac + "Item")
.OrderBy(el =>
el.Elements(cac + "RecommendedRetailPrice")
.Select(rrp => (double?) rrp.Elements(cbc + "PriceAmount"))
.FirstOrDefault() ?? 0.0
).Last();
创建一个方法来执行您的操作(我知道我知道我们都想要漂亮的Lambda表达式,而不是创建新的方法)
private static double GetPrice(XElement retail, string priceElement)
{
// short circuting will stop as soon as the statement is false
if (retail != null && retail.Element(priceElement) != null)
{
return Convert.ToDouble(retail.Element(priceElement).Value);
}
return 0.0;
}
你的链接现在看起来像这样棒…
var most_expensive = full_xml
.Element("ItemDetails")
.Elements(vco + "ItemDetail")
.Elements(cac + "Item")
.OrderBy(el => GetPrice(el.Element(cac + "RecommendedRetailPrice"), cbc + "PriceAmount"))
.Last();
我相信这个方法可以在其他需要获取价格的地方被重用,所以它将是一段有用的代码。
如果需要,还可以使用三元操作符使方法更短
private static double GetPrice(XElement retail, string priceElement)
{
return (retail != null && retail.Element(priceElement) != null) ?
Convert.ToDouble(retail.Element(priceElement).Value) : 0.0;
}
删除命名空间:
var doc = XDocument.Parse(@"
<ItemDetails>
<ItemDetail>
<Item>
<RecommendedRetailPrice>
<PriceAmount>4.95</PriceAmount>
</RecommendedRetailPrice>
</Item>
</ItemDetail>
</ItemDetails>
");
var result = doc
.Element("ItemDetails")
.Elements("ItemDetail")
.Elements("Item")
.OrderBy(item =>
item.Elements("RecommendedRetailPrice")
.Elements("PriceAmount")
.Select(pa => Convert.ToDouble(pa.Value,
CultureInfo.InvariantCulture))
.FirstOrDefault())
.Last();
Console.WriteLine(result);