代码之家  ›  专栏  ›  技术社区  ›  kitsune

如何保护LINQ to XML中的空引用异常?

  •  12
  • kitsune  · 技术社区  · 16 年前
    <?xml version="1.0" encoding="utf-8" ?>
    <pages> 
      <page id="56">
        <img id="teaser" src="img/teaser_company.png"></img>
      </page>  
    </pages>
    

    我有一个XML文件,它为CMS中的页面定义了额外的资源。使用linqtoxml查询此文件时,保护空引用异常的最佳方法是什么?

    var page = (from tabElement in extensionsDoc.Descendants("page")
    where tabElement.Attribute("id").Value == tabId.ToString()
    select tabElement).SingleOrDefault();
    

    如果页元素没有名为“id”的属性,则此代码可能会触发空引用异常。我必须使用try-catch块还是有办法处理这个问题?例如,如果page元素没有名为“id”的属性,则page对象的page返回空值。

    5 回复  |  直到 13 年前
        1
  •  28
  •   Jon Skeet    13 年前

    编辑:这显然是很久以前写的-这些天我肯定会按照伊戈尔的答案和演员一起去。

    最简单的方法是:

    var page = (from tabElement in extensionsDoc.Descendants("page")
                let idAttribute = tabElement.Attribute("id")
                where idAttribute != null 
                      && idAttribute.Value == tabId.ToString()
                select tabElement).SingleOrDefault();
    

    或者,您可以编写一个扩展方法 XElement :

    public static string AttributeValueOrDefault(this XElement element,
                                                 string attributeName)
    {
        XAttribute attr = element.Attribute(attributeName);
        return attr == null ? null : attr.Value;
    }
    

    然后使用:

    var page = (from element in extensionsDoc.Descendants("page")
                where element.AttributeValueOrDefault("id") == tabId.ToString()
                select element).SingleOrDefault();
    

    或使用点符号:

    var page = extensionsDoc.Descendants("page")
                 .Where(x => x.AttributeValueOrDefault("id") == tabId.ToString())
                 .SingleOrDefault();
    

    (打电话是有意义的 tabId.ToString() 预先一次,顺便说一句,而不是每次迭代。)

        2
  •  10
  •   Igor Zevaka    14 年前

    在.NET 4中,Linq to XML提供了一种实现这一点的方法,它是通过使用 explicit casts :

    var page = (
      from tabElement in extensionsDoc.Descendants("page")
        where (string)tabElement.Attribute("id") == tabId.ToString()
        select tabElement
    ).SingleOrDefault();
    

    如果属性不在那里,那么结果将仅仅是空的。

    除了明确的 string operator还有大多数基元类型及其可以为空的版本。这意味着你可以 AttributeOrDefault 使用这种语法:

    //<element theAttr="12" />
    int theAttr = (int?)doc.Element("element").Attribute("missingAttr") ?? 0;
    
        3
  •  3
  •   Gavin    16 年前

    我以前也见过其他人使用直接转换为字符串;我不知道它是否比jon建议的效率更高或更低,但我非常喜欢这种语法。

    var page = extensionsDoc.Descendants("page")
                 .Where(x => (string)x.Attribute("id") == tabId.ToString())
                 .SingleOrDefault();
    

    如果我的思想有缺陷,任何人都可以自由地解决这个问题;我对林肯是个新手。

        4
  •  1
  •   Kjetil Limkjær    15 年前

    我倾向于使用XPath表达式,否则代码会被大量空检查弄得乱七八糟。例如:

    var query = string.Format("page[@id='{0}']", tabId.ToString());
    var page = extensionsDoc.XPathSelectElement(query);
    
        5
  •  0
  •   Dino    13 年前

    我将使用一个类实体来映射XML元素。并调用一个检查空值的方法。我在代码中使用了这个方法,它工作得很好。希望它有帮助。

    下面是根据您的需要调整的示例代码:

    private void Method1(...) {
        ...
    
        var pages = from tabElement in extensionsDoc.Descendants("page")
        where tabElement.Attribute("id").Value == tabId.ToString()
        select new Page {
                    imgSrc = Method2(tabElement)
                };
    
        // pages variable is a List<Page> object
        ...
    }
    
    private void Method2(XElement element) {
        XElement img = element.Element("img");
    
        if (img != null) {
            ...
            // TODO return the imgSrc
            return "";
        }
    
        // return null or ""
        return null;
    }
    

    然后页面类定义:

    class Page
    {
        public string imgSrc { get; set; }
    }