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

How to check if another node exists in an xml without reading - C#

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

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

要检查 XmlReader 是否已正确定位,您可以检查是否 reader.NodeType == XmlNodeType.Element and reader.Name == EntityElementName。然后,如果 reader 已经正确定位,则不要使用 ReadToNextSibling().

向前扫描

但是,您的算法需要进行一些改进:

  1. 而不是检查 reader.Name 是否正确,而是检查 LocalName and NamespaceURI are as expected, and if not, call reader.ReadToNextSibling(string localName,string namespaceURI). This avoids hardcoding of namespace prefixes, which is a bug to be avoided.

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

综上所述,可以引入如下底层扩展方法:

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 时,涉及跳过节点的错误有时会被掩盖(因为空白节点被跳过。)