在C#中优化XML

时间:2009-05-21 16:44:33

标签: c# xml .net-3.5 linq-to-xml

背景

我们有一个在.NET 1.1中启动的项目,转移到.NET 2.0,最近再次移动到.NET 3.5。该项目极其数据驱动,并为其许多数据文件使用XML。其中一些XML文件非常庞大,我想借此机会改进应用程序与它们的交互。如果可能的话,我想避免在任何时候将它们完全保留在内存中,但另一方面,我想快速访问它们的数据。

当前设置使用XmlDocumentXPathDocument(取决于编写时间和由谁编写)。首次请求并在内部数据结构中缓存数据(而不是XML,在大多数情况下会占用更多内存)。在过去,这是一个很好的模型,因为它具有快速访问时间和低内存占用(或至少,令人满意的内存占用)。然而,现在有一个功能可以一次性查询大部分信息,而不是我们之前拥有的很好分散的请求。这会导致XML加载,验证和解析成为性能中可见的瓶颈。

问题

给定一个大型XML文件,在没有内存中的XML的情况下,重复查询其内容的最有效和最快速的方法是什么(例如“具有id = B的元素A是否存在?”)?

请注意,如果我们可以提供帮助,数据本身可以在内存中,而不是在更臃肿的XML格式中。在最坏的情况下,我们可以接受将一个文件加载到内存中进行解析,然后再次卸载以释放资源,但我想尽可能避免这种情况。

考虑到我们已经在尽可能地缓存数据,这个问题也可以理解为“哪个更快并且使用更少的内存; XmlDocumentXPathDocument,基于{{3}进行解析},或XmlReader / XDocument?“

编辑:更简单一点,我们可以随机访问磁盘上的XML而无需一次读取整个文件吗?

实施例

XML文件有一些记录:

<MyXml>
  <Record id='1'/>
  <Record id='2'/>
  <Record id='3'/>
</MyXml>

我们的用户界面想知道是否存在id为3的记录。如果可以的话,我们想要找出而不必解析并加载文件中的每条记录。因此,如果它在我们的缓存中,则没有XML交互,如果不是,我们可以将该记录加载到缓存中并响应请求。

目标

使用可扩展,快速的方式查询和缓存XML数据文件,以便我们的用户界面能够响应,而无需借助多个线程或将整个XML文件长期保留在内存中。

我意识到在某个地方可能会有关于此的博客或MSDN文章,我会在发布此问题之后继续使用Google,但是如果有人有一些可能有用的数据,或者某个方法的某些示例比另一个更好或更快,那将是伟大的。


更新
LINQ-to-XML为何时在.NET中使用各种XML API提供了很好的建议。看起来基于XmlReaderIEnumerable的内容将是我在此处提供的方案的最佳选择。

6 个答案:

答案 0 :(得分:2)

使用XML我只知道两种方式

XMLReader - &gt;流式传输大型XML数据 或者使用XML DOM对象模型并立即将整个XML读入内存。

如果XML很大,我们有80 MB范围及以上的XML文件,将XML读入内存是一个性能损失。没有真正的方法来“合并”处理XML文档的两种方式。遗憾。

答案 1 :(得分:2)

我不久前在试图流式传输XML时遇到过这篇白皮书:API-based XML streaming with FLWOR power and functional updates本文尝试使用内存XML但利用LINQ访问。

也许有人会发现它很有趣。

答案 2 :(得分:1)

这可能听起来很愚蠢 但是,如果您有简单的查询内容,则可以在xml文件中使用正则表达式。 (他们在unix / linux中做grep的方式)。

如果没有任何意义,我道歉。

答案 3 :(得分:0)

问题的第一部分听起来像是模式验证最有效。如果您可以访问XSD或可以创建它们,您可以使用类似于此的算法:

    public void ValidateXmlToXsd(string xsdFilePath, string xmlFilePath)
    {
        XmlSchema schema = ValidateXsd(xsdFilePath);
        XmlDocument xmlData = new XmlDocument();
        XmlReaderSettings validationSettings = new XmlReaderSettings();

        validationSettings.Schemas.Add(schema);
        validationSettings.Schemas.Compile();
        validationSettings.ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema;
        validationSettings.ValidationType = ValidationType.Schema;
        validationSettings.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);
        XmlReader xmlFile = XmlReader.Create(xmlFilePath, validationSettings);

        xmlData.Load(xmlFile);
        xmlFile.Close();
    }

    private XmlSchema ValidateXsd(string xsdFilePath)
    {
        StreamReader schemaFile = new StreamReader(xsdFilePath);
        XmlSchema schema = XmlSchema.Read(schemaFile, new ValidationEventHandler(ValidationHandler));
        schema.Compile(new ValidationEventHandler(ValidationHandler));
        schemaFile.Close();
        schemaFile.Dispose();

        return schema;
    }

    private void ValidationHandler(object sender, ValidationEventArgs e)
    {
        throw new XmlSchemaException(e.Message);
    }

如果xml无法验证,则会抛出XmlSchemaException

对于LINQ,我个人更愿意在XDocument时使用XmlDocument。你的目标有些主观,没有看到你正在做的事情,我不能说这样做,或者确切地说那会对你有所帮助。您可以将XPath与XDocument一起使用。我不得不说你应该使用最符合你需求的东西。有时使用XPath和LINQ其他时间都没有问题。这实际上取决于您的舒适度以及可扩展性和可读性。可以这么说,对团队有什么好处。

答案 4 :(得分:0)

XmlReader将使用比XmlDocument更少的内存,因为它不需要一次将整个XML加载到内存中。

答案 5 :(得分:0)

只是想一想JMarsch的评论。即使您的进程没有讨论XML生成,您是否考虑过将DB(或作为索引的XML文件的子集)作为中介?如果XML文件每天更新​​一次或两次,这显然只会带来好处。我想这需要权衡你现有的缓存机制。

我不能说速度,但由于语法原因,我更喜欢XDocument / LINQ。