简体   繁体   中英

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

I am having following type of XML:

在此输入图像描述

Through this XML I wish to populate following objectList.

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

where Function class is as follows,

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,

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. 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.

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:

  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:

    If an empty collection is passed to the Attributes extension method, it also returns an empty collection.

  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. In the case of First() , its variant is 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.

Case 1: Initialize with null

If you want to initialize them as null , you can do this:

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)

But that results in a lot of ugly null checking code. A simpler approach would be to just initialize with a list that contains 0 or more elements. 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:

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()
        // ...
    };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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