使用 DataContractSerializer 仅反序列化 XML 文档的一部分

Use DataContractSerializer to Deserialize only part of an XML document

假设我有一个执行以下操作的导出(序列化)函数

public void ExportToXML()
{
    var DCS = new DataContractSerializer(typeof(Entry));
    var XWriter = XmlWriter.Create(@"C:\Temp\Export.xml");
    XWriter.WriteStartDocument();
    XWriter.WriteStartElement("Entries");
    Entries.ForEach(e =>
    {
        DCS.WriteStartObject(XWriter, e);
        DCS.WriteObjectContent(XWriter, e);
        DCS.WriteEndObject(XWriter);
    });
    XWriter.WriteEndElement();
    XWriter.WriteEndDocument();
    XWriter.Close();
}

导出一个看起来像

的 XML 文件
<Entries>
  <Entry>{Some Data}</Entry>
  <Entry>{Some Data}</Entry>
  <Entry>{Some Data}</Entry>
  <Entry>{Some Data}</Entry>
</Entries>

对于 Import 方法,我想一次反序列化每个

<Entry>{Some Data}</Entry>
一个,以便我可以应用转换

Func<Entry,Entry>

如果一个

Func<Entry,bool>

谓词为真

这是我想出来的

public void ImportFromXML(string FileName, Func<Entry,Entry> Transform, Func<Entry,bool> DoTransform)
{
    var DCS = new DataContractSerializer(typeof(Entry));
    var ImportedEntries = new List<Entry>();
    foreach (var EntryElement in XDocument.Load(FileName).Root.Elements().Where(xe => xe.Name.LocalName == "Entry")) 
    {
        var XMLEntry = (Entry)DCS.ReadObject(EntryElement.CreateReader());
        ImportedEntries.Add(DoTransform(XMLEntry) ? Transform(XMLEntry) : XMLEntry);
    }
    entries = ImportedEntries.ToDictionary(e => e.KeyName + "\" + e.ValueName);
}

这可行,但我想知道是否有一种方法可以使用单个 XmlReader 一次性完成此操作,而不是生成每个 XElement 的 XMLReader。

我试图反转导出方法的逻辑

public void ImportFromXML(string FileName, Func<Entry,Entry> Transform, Func<Entry,bool> DoTransform)
{        
    var DCS = new DataContractSerializer(typeof(Entry));
    var ImportedEntries = new List<Entry>();
    var XReader = XmlReader.Create(@"C:\Temp\Export.xml");
    XReader.ReadStartElement("Entries");
    while (!{WHAT Exit Condition?})
    {
        var XMLEntry = (Entry)DCS.ReadObject(XReader());
        ImportedEntries.Add(DoTransform(XMLEntry) ? Transform(XMLEntry) : XMLEntry);
    }
    XReader.Close();
    entries = ImportedEntries.ToDictionary(e => e.KeyName + "\" + e.ValueName);
}

但是我不确定要为

{WHAT Exit Condition?}
添加什么,显然我不能使用

!XReader.EOF
因为读到文件末尾会导致它尝试反序列化结束 标记作为条目。

class 这些方法的一部分将作为我们的 SCCM OS 部署任务序列的一部分使用,这意味着它们可以被多个并发 运行 任务序列使用正在通过网络查询源 XML 文件。所以我有点担心更好的性能。

我是在追着我的尾巴尝试使用单个 XmlReader 执行此操作,还是将 LINQ to XML 与单独的 XmlReader 结合使用是最佳选择?

如果您的转换足够规则,我将采用 XSLT 转换并将其应用于源文档的方法。您可以制作 xml 或非 xml 输出。如果您从未使用过它,那就有点奇怪了……但是您编写了 xsl:template 个元素 select 输入,然后嵌套 xsl:apply-template 个描述如何转换 selected 的元素件。听起来是为您的问题量身定制的。

一个不错的例子,在相关问题中可以找到here, and a good 50,000 ft overview of using XSLT here

XML文件可以写有缩进和无缩进。在前一种情况下,以下代码可以正常工作:

var settings = new XmlWriterSettings { Indent = true };
var DCS = new DataContractSerializer(typeof(Entry));

using (var writer = XmlWriter.Create(fileName, settings)) // with indentation
{
    writer.WriteStartDocument();
    writer.WriteStartElement("Entries");

    foreach (var entry in Entries)
    {
        DCS.WriteObject(writer, entry);
    }
}

using (var reader = XmlReader.Create(fileName))
{
    while (reader.ReadToFollowing("Entry"))
    {
        var xmlEntry = (Entry)DCS.ReadObject(reader);
        // ...
    }
}

在这种情况下,ReadToFollowing 方法首先读取空格,然后前进到下一个Entry 节点。但是在没有缩进的情况下,该方法会跳过一个 Entry 节点。

对于后一种情况,我们可以使用如下代码:

using (var writer = XmlWriter.Create(fileName)) // without indentation
// ...


using (var reader = XmlReader.Create(fileName))
{
    while (reader.LocalName == "Entry" || reader.ReadToFollowing("Entry"))
    {
        var xmlEntry = (Entry)DCS.ReadObject(reader);
        // ...
    }
}

此外,这段代码在这两种情况下都能正常工作。