1

我正在使用 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,它会立即将其发送到其他方法,而不必先创建它们的完整列表?

4

3 回答 3

3

XDocument.Load将首先将整个文件加载到内存中。IEnumerable对于CaseFire 对象的快速搜索元素返回并用于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
}
于 2012-12-10T20:19:33.193 回答
0

只需删除 ToList 并将您的变量声明为 IEnumerable(或仅使用 var)就可以解决问题。尽管将所有文件加载到内存中,但对于大文件可能不会指示使用 linq to XML。您应该考虑改用 à XmlReader

于 2012-12-10T20:15:49.133 回答
0

这里的问题是您在 LINQ 表达式的结果上调用 ToList。这将枚举所有值并将所有内容写入内存。LINQ-Expression 本身不会枚举这些值。它只会返回一个 IEnumerable。LINQ 表达式不会创建任何 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 对象。

于 2012-12-10T20:26:04.627 回答