如何在不读取的情况下检查 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()
.
向前扫描
但是,您的算法需要进行一些改进:
而不是检查 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.
而不是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 时,涉及跳过节点的错误有时会被掩盖(因为空白节点被跳过。)
我想实现将 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()
.
但是,您的算法需要进行一些改进:
而不是检查
reader.Name
是否正确,而是检查LocalName
andNamespaceURI
are as expected, and if not, callreader.ReadToNextSibling(string localName,string namespaceURI)
. This avoids hardcoding of namespace prefixes, which is a bug to be avoided.而不是
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 时,涉及跳过节点的错误有时会被掩盖(因为空白节点被跳过。)