我想实现将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);
}
答案 0 :(得分:0)
要检查XmlReader
是否已正确定位,您可以检查reader.NodeType == XmlNodeType.Element
和reader.Name == EntityElementName
。然后,如果读者已经正确定位,请不要使用ReadToNextSibling()
向前扫描。
但是,您的算法有一些改进:
不要检查正确的reader.Name
,而是检查LocalName
和NamespaceURI
是否符合预期,如果没有,请致电reader.ReadToNextSibling(string localName,string namespaceURI)
。这避免了名称空间前缀的硬编码,这是bug to be avoided。
而不是ReadOuterXml()
,请致电reader.ReadSubtree()
并将返回的读者直接传递给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时有时会屏蔽涉及跳过节点的错误(因为跳过了空白节点。)