如何使用XmlReader解析XML及其结束标记?

时间:2013-12-31 09:59:28

标签: c# xml-parsing xmlreader

考虑我必须解析的以下XML。

<root>
  <item>
    <itemId>001</itemId>
    <itemName>test 1</itemName>
    <description/>
  </item>
</root>

我必须解析每个标记并将其存储到表中,如下所示:

TAG_NAME        TAG_VALUE         IsContainer
------------    --------------    -----------
root            null              true
item            null              true
itemId          001               false
itemName        test 1            false
description     null              false
/item           null              true
/root           null              true

现在要完成这项工作,我正在使用XmlReader,因为这样我们就可以解析每个&amp;每个节点。

我这样做:

我创建了以下类来包含每个标记的数据

public class XmlTag
{
  public string XML_TAG { get; set; }      
  public string XML_VALUE { get; set; }      
  public bool IsContainer { get; set; }
}

我正在尝试获取标签列表(包括关闭标签),如下所示:

    private static List<XmlTag> ParseXml(string path)
    {
        var tags = new List<XmlTag>();

        using (var reader = XmlReader.Create(path))
        {
            while (reader.Read())
            {
                var tag = new XmlTag();
                bool shouldAdd = false;
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        shouldAdd = true;
                        tag.XML_TAG = reader.Name;

                        //How do I get the VALUE of current reader?
                        //How do I determine if the current node contains children nodes to set IsContainer property of XmlTag object?
                        break;
                    case XmlNodeType.EndElement:
                        shouldAdd = true;
                        tag.XML_TAG = string.Format("/{0}", reader.Name);
                        tag.XML_VALUE = null;
                        //How do I determine if the current closing node belongs to a node which had children.. like ROOT or ITEM in above example?
                        break;
                }

                if(shouldAdd)
                    tags.Add(tag);
            }
        }

        return tags;
    }

但我很难确定以下内容:

  1. 如何确定当前ELEMENT是否包含子XML节点?设置IsContainer属性。
  2. 如果类型为XmlNodeType.Element
  3. ,则如何获取当前节点值的值

    修改

    我尝试使用LINQ to XML,如下所示:

    var xdoc = XDocument.Load(@"SampleItem.xml");
    
    var tags = (from t in xdoc.Descendants()
                select new XmlTag
                {
                    XML_TAG = t.Name.ToString(),
                    ML_VALUE = t.HasElements ? null : t.Value,
                    IsContainer = t.HasElements
                }).ToList();
    

    这给了我XML标签及其值,但这并没有给我所有标签,包括结束标签。这就是我决定尝试XmlReader的原因。但如果我错过了LINQ to XML示例中的任何内容,请更正我。

1 个答案:

答案 0 :(得分:2)

首先,正如Jon Skeet in the comments所述,您应该考虑使用其他工具,例如XmlDocument可能与LINQ to XML一起使用(编辑 :跟随XmlDocument的示例。)

话虽如此,这是目前最简单的解决方案(请注意,这不是最干净的代码,并且没有太多验证):

private static List<XmlTag> ParseElement(XmlReader reader, XmlTag element)
{
    var result = new List<XmlTag>() { element };
    while (reader.Read())
    {
        switch (reader.NodeType)
        {
            case XmlNodeType.Element:
                element.IsContainer = true;
                var newTag = new XmlTag() { XML_TAG = reader.Name };
                if (reader.IsEmptyElement)
                {
                    result.Add(newTag);
                }
                else
                {
                    result.AddRange(ParseElement(reader, newTag));
                }
                break;
            case XmlNodeType.Text:
                element.XML_VALUE = reader.Value;
                break;
            case XmlNodeType.EndElement:
                if (reader.Name == element.XML_TAG)
                {
                    result.Add(new XmlTag()
                        {
                            XML_TAG = string.Format("/{0}", reader.Name),
                            IsContainer = element.IsContainer
                        });
                }

                return result;
        }
    }

    return result;
}

private static List<XmlTag> ParseXml(string path)
{
    var result = new List<XmlTag>();

    using (var reader = XmlReader.Create(path))
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                result.AddRange(ParseElement(
                    reader,
                    new XmlTag() { XML_TAG = reader.Name }));
            }
            else if (reader.NodeType == XmlNodeType.EndElement)
            {
                result.Add(new XmlTag() 
                    { 
                        XML_TAG = string.Format("/{0}",current.Name)
                    });
            }
        }
    }

    return result;
}

使用XmlDocument的示例。对于自封闭标记(在您的情况下为<description/>),这会产生略微不同的结果。您可以根据需要轻松更改此行为。

private static IEnumerable<XmlTag> ProcessElement(XElement current)
{
    if (current.HasElements)
    {
        yield return new XmlTag() 
            { 
                XML_TAG = current.Name.ToString(),
                IsContainer = true
            };

        foreach (var tag in current
            .Elements()
            .SelectMany(e => ProcessElement(e)))
        {
            yield return tag;
        }

        yield return new XmlTag() 
            { 
                XML_TAG = string.Format("/{0}", current.Name.ToString()),
                IsContainer = true
            };
    }
    else
    {
        yield return new XmlTag()
            { 
                XML_TAG = current.Name.ToString(), 
                XML_VALUE = current.Value
            };

        yield return new XmlTag()
            {
                XML_TAG = string.Format("/{0}",current.Name.ToString())
            };
    }
}

使用它:

var xdoc = XDocument.Load(@"test.xml");
var tags = ProcessElement(xdoc.Root).ToList();