Linq to XML单独处理结果而不是列出

时间:2012-12-10 19:56:45

标签: c# xml linq

我正在使用C#中的解析器从多个xml文件中获取数据并将它们放入我自己的数据库中。

现在我的代码有效:

 List<CaseFile> caseFiles =
            (
                from e in XDocument.Load(xmlDoc).Root.Elements("application-information").Elements("file-segments").Elements("action-keys").Elements("case-file")
                select new CaseFile
                { (......)
}).toList();

这将创建一个CaseFile对象列表,我稍后将其发送到其他方法以将其数据放入我的数据库中。这个问题是我需要一次解析许多文件,有些文件大到1GB,没有小于200MB,所以这会产生巨大的内存需求。

有没有办法修改我的语句,所以对于它找到的每个CaseFile,它会立即将它发送到其他方法而不必先创建它们的完整列表?

3 个答案:

答案 0 :(得分:3)

XDocument.Load将首先将整个文件加载到内存中。对于快速搜索元素,返回CaseFire对象的IEnumerable并使用XmlReader逐节点读取文件(假设您case-file只有root/application-information/file-segments/action-keys个节点,否则您需要更多逻辑):

static IEnumerable<CaseFile> FindCaseFiles(string uri)
{
    using (XmlReader reader = XmlReader.Create(uri))
    {
        reader.MoveToContent();

        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    if (reader.Name == "case-file")
                    {
                        XElement el = XElement.ReadFrom(reader) as XElement;
                        if (el != null)
                            yield return new CaseFile() {...};
                    }
                    break;
            }
        }
    }
}

用法:

foreach(CaseFile file in FindCaseFiles(path_to_xml))
{
    // we have CaseFile here immediately after it found in xml file
}

答案 1 :(得分:0)

只需删除ToList并将您的变量声明为IEnumerable(或者只是使用var)即可。 虽然因为它将所有文件加载到内存中,但可能不会为大文件指示使用linq到XML。您应该考虑使用àXmlReader

答案 2 :(得分:0)

这里的问题是你在LINQ-Expression的结果上调用ToList。这将枚举所有值并将所有内容写入内存。 LINQ-Expression本身不会枚举值。它只会返回一个IEnumerable。 LINQ-Expression不会创建CaseFile对象。

如果只是遍历LINQ-Expression的结果,它将读取另一个值:

var caseFiles = from e in XDocument.Load(xmlDoc).Root.
                    Elements("application-information").
                    Elements("file-segments").
                    Elements("action-keys").
                    Elements("case-file")
                select new CaseFile { ... };

foreach (CaseFile cf in caseFiles)
{
    DoTheWork(cf);
}

这样,任何时候都只会创建一个CaseFile对象。