如何使用XmlReader解析XML并包含它们的闭合标签?

3

考虑以下需要解析的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,因为它允许我们解析每一个节点。
我的做法如下:
我创建了以下类来包含每个标签的数据。
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 类型,如何获取其值。

编辑:

我尝试使用 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示例中漏掉了什么,请纠正我。


3
在第一步,您必须使用XmlReader吗?除非您担心将大文件加载到内存中,否则使用LINQ to XML(甚至只是XmlDocument)会使生活更加简单。 - Jon Skeet
我不必使用XmlReader,而且文件根本不是很大。我尝试使用XmlReader的唯一原因是因为表格结构。我必须读取所有的XML标签(包括闭合标签)。如果您能给我一个使用LINQ to XML或XmlDocument的小例子,我将非常感激。谢谢。 - Aamir
@JonSkeet - 你能否为我们详细说明或提供一些链接,解释为什么我们不应该使用XmlReader? - Aamir
1个回答

2
首先,正如Jon Skeet评论中所述,你可能应该考虑使用其他工具,比如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();    

我已经更新了你的答案,并进行了一些小修改以完全符合我的需求。很抱歉没有事先询问你。 - Aamir
@Aamir 没关系,这是正确的做法。我看到其他人拒绝了你的编辑(你编辑了改变原始含义 - 格式和方法顺序通常应该保持原样) - 我会尝试将你的一些修改合并到帖子中。如果你发现可以改进的内容,请随时再次编辑它 - 如果我在场,我会批准它。 - BartoszKP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接