如何在没有读取的情况下检查xml中是否存在另一个节点 - C#

时间:2017-07-21 18:44:41

标签: c# xml xmlreader

我想实现将xml反序列化为对象列表的代码。我在代码中发现了一个问题,其中while向前读取,因此每个其他节点都被跳过。检查xml中的下一个节点以在此代码的while循环中实现的正确方法是什么?

private Task<List<TAxEntity>> Deserialize(XmlReader reader)
    {
        var deserializer = new XmlSerializer(typeof(TAxEntity));
        var entities = new List<TAxEntity>();

        do
        {
            using (var stringReader = new StringReader(reader.ReadOuterXml()))
            {
                var entity = (TAxEntity)deserializer.Deserialize(stringReader);

                entities.Add(entity);
            }
        }
        while (reader.ReadToNextSibling(EntityElementName));

        return Task.FromResult(entities);
    }

1 个答案:

答案 0 :(得分:0)

要检查XmlReader是否已正确定位,您可以检查reader.NodeType == XmlNodeType.Elementreader.Name == EntityElementName。然后,如果读者已经正确定位,请不要使用ReadToNextSibling()向前扫描。

但是,您的算法有一些改进:

  1. 不要检查正确的reader.Name,而是检查LocalNameNamespaceURI是否符合预期,如果没有,请致电reader.ReadToNextSibling(string localName,string namespaceURI)。这避免了名称空间前缀的硬编码,这是bug to be avoided

  2. 而不是ReadOuterXml(),请致电reader.ReadSubtree()并将返回的读者直接传递给deserializer.Deserialize()。您当前的算法解析XML,将其重新格式化为第二个XML字符串,然后再次解析该字符串。使用ReadSubtree()允许XmlSerializer直接从传入的XmlReader流式传输嵌套元素,从而避免这种额外的解析和重新格式化。

  3. 将所有这些放在一起,您可以引入以下较低级别的扩展方法:

    public static class XmlReaderExtensions
    {
        public static IEnumerable<TElement> DeserializeSequence<TElement>(this XmlReader reader, string localEntityElementName, string namespaceURI)
        {
            if (reader == null)
                throw new ArgumentNullException();
            var deserializer = new XmlSerializer(typeof(TElement));
            while ((reader.NodeType == XmlNodeType.Element && reader.LocalName == localEntityElementName && reader.NamespaceURI == namespaceURI)
                || reader.ReadToNextSibling(localEntityElementName, namespaceURI))
            {
                // Using ReadSubtree instead of ReadOuterXml() avoids having do parse, reformat, then parse the formatted XML a second time
                // by reading directly from the current stream only once.
                TElement element;
                using (var subReader = reader.ReadSubtree())
                {
                    element = (TElement)deserializer.Deserialize(subReader);
                }
                // Consume the EndElement also (or move past the current element if reader.IsEmptyElement).
                reader.Read();
                yield return element;
            }
        }
    }
    

    并将您的Deserialize()方法修改为如下:

        private Task<List<TAxEntity>> Deserialize(XmlReader reader)
        {
            var entities = reader.DeserializeSequence<TAxEntity>(EntityElementName, "" /* Pass the correct namespace here */).ToList();
    
            return Task.FromResult(entities);
        }       
    

    示例.Net fiddle

    请注意,任何手动XmlReader代码都应该使用缩进和非缩进XML进行单元测试,因为在解析缩进的XML时有时会屏蔽涉及跳过节点的错误(因为跳过了空白节点。)