【问题标题】:how to parse XML using XmlReader along with their closing tags?如何使用 XmlReader 及其结束标记解析 XML?
【发布时间】:2014-01-18 06:39:27
【问题描述】:

考虑以下我必须解析的 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 示例中的任何内容,请纠正我。

【问题讨论】:

  • 首先使用XmlReader吗?除非您担心将大量文件加载到内存中,否则使用 LINQ to XML(甚至只是 XmlDocument)将使生活变得更加简单。
  • 我不必使用 XmlReader,文件也不是很大。我尝试 XmlReader 的唯一原因是表结构。我必须阅读所有 XML 标记(包括结束标记)。如果您能给我一个关于如何使用 LINQ to XML 或 XmlDocument 的小例子,我将不胜感激。谢谢
  • @JonSkeet - 您能否详细说明或为我们提供一些链接,为什么我们不应该使用 XmlReader?

标签: c# xml-parsing xmlreader


【解决方案1】:

首先,正如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 的示例。对于自封闭标签(在您的情况下为&lt;description/&gt;),这将给出稍微不同的结果。您可以根据需要轻松更改此行为。

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 没关系,这是正确的做法。我看到您的编辑被其他人拒绝(您编辑更改了原始含义 - 格式和方法的顺序通常应该保持原始状态) - 我会尝试将您的一些修改合并到帖子中。如果您发现可以改进的地方,请随时再次编辑它 - 如果我愿意,我会批准它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-27
  • 2016-12-11
相关资源
最近更新 更多