Как анализировать необязательные или нулевые XML-элементы и атрибуты с помощью LINQ? - PullRequest
0 голосов
/ 09 декабря 2011

У меня следующий тип XML:

enter image description here

В этом XML я хочу заполнить следующий objectList.

List<Function> objFunctionsList = new List<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; }
}

, а класс Parameter выглядит следующим образом:

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

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

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

Вы можете видеть, что в XML некоторые теги функций имеют тег Parameters, а другие - нет.Я пробовал это:

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);
        }
    }
}

, но это код на основе XPath, который использовался ранее, когда у меня не было тега параметров в теге функции.

Ответы [ 2 ]

2 голосов
/ 09 декабря 2011
0 голосов
/ 29 марта 2014

Как указывает статья, которую @NitinRastogi упоминает в его ответ , вы можете использовать некоторые удобные свойства LINQ, чтобы упростить работу вокруг null или дополнительные значения:

  1. Методы, которые возвращают коллекции, такие как Elements(), будут возвращать пустую коллекцию (не null), если результаты не найдены или переданыпустые коллекции в качестве входных данных:

    Если в метод расширения Attributes передается пустая коллекция, она также возвращает пустую коллекцию.

  2. Некоторые методы LINQ, которые возвращают одно значение, например First(), также имеют варианты, которые будут возвращать значение по умолчанию, например null, если объект не найден, вместо вызова исключения.В случае First() его вариант равен FirstOrDefault().

Так что в вашем случае, поскольку Parameters являются необязательными для ваших Function объектовВы можете либо инициализировать их с Parameters, равным null, либо пустым списком.

Случай 1: Инициализировать с помощью null

Если вы хотите инициализировать их как 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()
        // ...
    };

Случай 2: Инициализировать списком (0 или более элементов)

Но это приводит к большому количеству уродливого null кода проверки.Более простой подход состоит в том, чтобы просто инициализировать списком, который содержит 0 или более элементов.Все эти методы сбора LINQ и XElement будут возвращать пустую коллекцию, если нет параметров, поэтому свойство Parameters в ваших функциях будет просто пустым списком вместо нуля:

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