简体   繁体   English

如何使用LINQ解析可选或空的XML元素和属性?

[英]How to Parse Optional or Null XML Elements & Attributes with LINQ?

I am having following type of XML: 我有以下类型的XML:

在此输入图像描述

Through this XML I wish to populate following objectList. 通过这个XML我希望填充以下objectList。

List<Function> objFunctionsList = new List<Function>();

where Function class is as follows, 其中Function类如下,

public class Function
{
    public String Name { get ; set; }
    public Parameter ReturnType { get; set; }
    public List<Parameter> Parameters { get; set; }
    public String Library { get; set; }
    public String Signature { get; set; }
    public String Description { get; set; }
    public String Code { get; set; }
}

and the Parameter class is as follows, 而Parameter类如下,

public class Parameter
{
    [DefaultValue("")] 
    public String Name { get; set; }

    [DefaultValue("")]
    public String DataType { get; set; }

    [DefaultValue("")]
    public String OccurenceType { get; set; }
}

You can see that in XML some function tags have Parameters tag while some others do not. 您可以看到,在XML中,某些函数标记具有Parameters标记,而其他一些函数标记则没有。 I have tried this: 我试过这个:

public const string XPATH_NAME = "/Functions/Function/Name";
public const string XPATH_LIBRARY = "/Functions/Function/Library";
public const string XPATH_SIGNATURE = "/Functions/Function/Signature";
public const string XPATH_DESCRIPTION = "/Functions/Function/Description";
public const string XPATH_CODE = "/Functions/Function/Code";

List<Function> objFunctionsList = new List<Function>();

try
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.Load(pXMLPath);

    XmlNodeList nlName = xmlDoc.SelectNodes(Constants.XPATH_NAME);
    XmlNodeList nlLibrary = xmlDoc.SelectNodes(Constants.XPATH_LIBRARY);
    XmlNodeList nlSignature = xmlDoc.SelectNodes(Constants.XPATH_SIGNATURE);
    XmlNodeList nlDescription = xmlDoc.SelectNodes(Constants.XPATH_DESCRIPTION);
    XmlNodeList nlCode = xmlDoc.SelectNodes(Constants.XPATH_CODE);

    // Name, Signature, Library, element should be present in 'Function' node
    if (nlName.Count == nlLibrary.Count
        && nlName.Count == nlSignature.Count
        && nlName.Count == nlDescription.Count
        && nlName.Count == nlCode.Count)
    {
        for (int iCount = 0; iCount < nlName.Count; iCount++)
        {
            Function objFunction = new Function();
            objFunction.Name = nlName[iCount].InnerText.Trim();
            objFunction.Library = nlLibrary[iCount].InnerText.Trim();
            string signature = nlSignature[iCount].InnerText;

            Parameter objReturnType = new Parameter();
            string returnType = (nlSignature[iCount].Attributes[Constants.ATRR_TYPE] == null
                ? Constants.XSNOPARAM
                : nlSignature[iCount].Attributes[Constants.ATRR_TYPE].Value);

            if (returnType.EndsWith(Constants.ASTERIK))
            {
                objReturnType.DataType = returnType.Substring(0, returnType.Length - 1);
                objReturnType.OccurenceType = Constants.OCCURENCES_ASTERISK;
            }
            else if (returnType.EndsWith(Constants.PLUS))
            {
                objReturnType.DataType = returnType.Substring(0, returnType.Length - 1);
                objReturnType.OccurenceType = Constants.OCCURENCES_PLUS;
            }
            else if (returnType.EndsWith(Constants.QUESTION_MARK))
            {
                objReturnType.DataType = returnType.Substring(0, returnType.Length - 1);
                objReturnType.OccurenceType = Constants.OCCURENCES_QUESTION;
            }
            else if (returnType.Length > 0)
            {
                objReturnType.DataType = returnType;
            }

            objFunction.ReturnType = objReturnType;

            objFunction.Parameters = new List<Parameter>();

            objFunction.Signature = signature;
            objFunction.Description = nlDescription[iCount].InnerText.Trim();
            objFunction.Code = nlCode[iCount].InnerText.Trim();

            objFunctionsList.Add(objFunction);
        }
    }
}

but this is XPath based code and was in use earlier when I was not having Parameters Tag in the function tag. 但这是基于XPath的代码,并且在我没有在函数标记中使用参数标记时使用。

As the article that @NitinRastogi mentions in his answer points out, you can use some convenient properties of LINQ to make it easy to work around null or optional values: 正如@NitinRastogi他的回答中 提到的那篇文章所指出的那样,你可以使用LINQ的一些方便属性来简化 null或可选值:

  1. Methods that return collections, such as Elements() , will return an empty collection (not null ) if no results are found or they're passed empty collections as input: 如果没有找到结果或者将空集合作为输入传递,则返回集合的方法(如Elements()将返回空集合(非null ):

    If an empty collection is passed to the Attributes extension method, it also returns an empty collection. 如果将空集合传递给Attributes扩展方法,它还会返回一个空集合。

  2. Some LINQ mehods that return a single value, such as First() , also have variants that will return a default value, such as null , if no object is found, instead of raising an exception. 一些返回单个值的LINQ方法First()First()也有变量,如果没有找到对象,将返回默认值,如null ,而不是引发异常。 In the case of First() , its variant is FirstOrDefault() . First()的情况下,它的变体是FirstOrDefault()

So in your case, since Parameters are optional for your Function objects, you can choose to either initialize them with Parameters equal to null or an empty list. 因此,在您的情况下,由于Parameters对于Function对象是可选的,您可以选择使用等于null Parameters或空列表初始化它们。

Case 1: Initialize with null 案例1:使用null初始化

If you want to initialize them as null , you can do this: 如果要将它们初始化为null ,则可以执行以下操作:

var doc = // XDocument.Load() or XDocument.Parse() ...
var functions =
    from f in doc.Root.Elements()
    select new
    {
        // ...
        Parameters = f.Elements("parameters").Elements().Count() == 0
            ? null
            : (
                from p in f.Elements("parameters").Elements()
                select new {
                    DataType = p.Attribute("type"),
                    Name = p.Attribute("name"),
                    Occurence = p.Attribute("occurence")
              })
              .ToList()
        // ...
    };

Case 2: Initialize with list (0 or more elements) 情况2:使用列表初始化(0个或更多元素)

But that results in a lot of ugly null checking code. 但这导致了很多丑陋的null检查代码。 A simpler approach would be to just initialize with a list that contains 0 or more elements. 一种更简单的方法是使用包含0个或更多元素的列表进行初始化。 These LINQ and XElement collection methods will all return an empty collection if there are no parameters, so that the Parameters property on your functions will just be an empty list instead of null: 如果没有参数,这些LINQ和XElement集合方法都将返回一个空集合,因此函数的Parameters属性将只是一个空列表而不是null:

var doc = // XDocument.Load() or XDocument.Parse() ...
var functions =
    from f in doc.Root.Elements()
    select new
    {
        // ...
        Parameters = (
            from p in f.Elements("parameters").Elements()
            select new {
                DataType = p.Attribute("type"),
                Name = p.Attribute("name"),
                Occurence = p.Attribute("occurence")
            })
            .ToList()
        // ...
    };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM