2

我正在处理以下结构的 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. 不过,我想Items按它们各自的RecommendedRetailPrices. 所以我最终得到了以下代码,它工作正常,但必须在一些可怕的嵌套 if 语句中的每个阶段检查空值:

//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 层的可选子节点对某些内容进行排序。

任何帮助表示赞赏。

4

3 回答 3

0

如果可以存在零个或一个元素,那么我会使用Elements()而不是Element()选择FirstOrDefault. 然后使用显式转换运算符ofXElement来获取您的值。

像这样的东西

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();
于 2013-06-11T09:34:44.523 回答
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);
于 2013-06-11T11:11:32.917 回答
0

创建一个方法来执行您的操作(我知道我知道我们都想制作漂亮的 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;
}

你的 linq 现在看起来很棒......

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;
}
于 2013-06-11T09:42:53.577 回答