Каков самый быстрый способ загрузки XML в XDocument?

Когда вы создаете новый XDocument с помощью XDocument.Load, он открывает файл XML и сохраняет локальную копию или постоянно читает документ с жесткого диска? Если он постоянно читает, есть ли более быстрый способ разбора XML?

XDocument x = XDocument.Load("file.xml");

Ответы

Ответ 1

Существует несколько измерений:

  • Линейная скорость обхода (например, чтение/загрузка)
  • Скорость запроса по запросу

Чтобы ответить на ближайший вопрос: XDocument использует XmlReader для загрузки документа в память, читая каждый элемент и создавая соответствующие экземпляры XElement (см. код ниже). Таким образом, он должен быть довольно быстрым (достаточно быстро для большинства целей), но при анализе большого документа он может потреблять большой объем памяти.

Необработанный XmlReader - отличный выбор для обхода, если ваши потребности ограничены тем, что можно сделать, не сохраняя документ в памяти. Он будет превосходить другие методы, поскольку никакая значительная структура не создается и не разрешается относительно других узлов (например, связывание родительского и дочернего узлов). Однако возможность запроса по требованию почти не существует; вы можете реагировать на значения, найденные в каждом node, но вы не можете запросить документ в целом. Если вам нужно взглянуть на документ во второй раз, вам нужно снова пройти все это.

Для сравнения, XDocument займет больше времени, чтобы пройти, потому что он создает новые объекты и выполняет основные структурные задачи. Он также будет потреблять память пропорционально размеру источника. В обмен на эти компромиссы вы получаете отличные возможности запросов.

Возможно, можно комбинировать подходы, как упомянутые Jon Skeet и показанные здесь: Потоковая передача в LINQ to XML с использованием пользовательских итераторов С# и XmlReader.

Источник для загрузки XDocument()

public static XDocument Load(Stream stream, LoadOptions options)
{
    XmlReaderSettings xmlReaderSettings = XNode.GetXmlReaderSettings(options);
    XDocument result;
    using (XmlReader xmlReader = XmlReader.Create(stream, xmlReaderSettings))
    {
        result = XDocument.Load(xmlReader, options);
    }
    return result;
}

// which calls...

public static XDocument Load(XmlReader reader, LoadOptions options)
{
    if (reader == null)
    {
        throw new ArgumentNullException("reader");
    }
    if (reader.ReadState == ReadState.Initial)
    {
        reader.Read();
    }
    XDocument xDocument = new XDocument();
    if ((options & LoadOptions.SetBaseUri) != LoadOptions.None)
    {
        string baseURI = reader.BaseURI;
        if (baseURI != null && baseURI.Length != 0)
        {
            xDocument.SetBaseUri(baseURI);
        }
    }
    if ((options & LoadOptions.SetLineInfo) != LoadOptions.None)
    {
        IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo;
        if (xmlLineInfo != null && xmlLineInfo.HasLineInfo())
        {
            xDocument.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
        }
    }
    if (reader.NodeType == XmlNodeType.XmlDeclaration)
    {
        xDocument.Declaration = new XDeclaration(reader);
    }
    xDocument.ReadContentFrom(reader, options);
    if (!reader.EOF)
    {
        throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedEndOfFile"));
    }
    if (xDocument.Root == null)
    {
        throw new InvalidOperationException(Res.GetString("InvalidOperation_MissingRoot"));
    }
    return xDocument;
}

// which calls...

internal void ReadContentFrom(XmlReader r, LoadOptions o)
{
    if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == LoadOptions.None)
    {
        this.ReadContentFrom(r);
        return;
    }
    if (r.ReadState != ReadState.Interactive)
    {
        throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedInteractive"));
    }
    XContainer xContainer = this;
    XNode xNode = null;
    NamespaceCache namespaceCache = default(NamespaceCache);
    NamespaceCache namespaceCache2 = default(NamespaceCache);
    string text = ((o & LoadOptions.SetBaseUri) != LoadOptions.None) ? r.BaseURI : null;
    IXmlLineInfo xmlLineInfo = ((o & LoadOptions.SetLineInfo) != LoadOptions.None) ? (r as IXmlLineInfo) : null;
    while (true)
    {
        string baseURI = r.BaseURI;
        switch (r.NodeType)
        {
        case XmlNodeType.Element:
        {
            XElement xElement = new XElement(namespaceCache.Get(r.NamespaceURI).GetName(r.LocalName));
            if (text != null && text != baseURI)
            {
                xElement.SetBaseUri(baseURI);
            }
            if (xmlLineInfo != null && xmlLineInfo.HasLineInfo())
            {
                xElement.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
            }
            if (r.MoveToFirstAttribute())
            {
                do
                {
                    XAttribute xAttribute = new XAttribute(namespaceCache2.Get((r.Prefix.Length == 0) ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value);
                    if (xmlLineInfo != null && xmlLineInfo.HasLineInfo())
                    {
                        xAttribute.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
                    }
                    xElement.AppendAttributeSkipNotify(xAttribute);
                }
                while (r.MoveToNextAttribute());
                r.MoveToElement();
            }
            xContainer.AddNodeSkipNotify(xElement);
            if (r.IsEmptyElement)
            {
                goto IL_30A;
            }
            xContainer = xElement;
            if (text != null)
            {
                text = baseURI;
                goto IL_30A;
            }
            goto IL_30A;
        }
        case XmlNodeType.Text:
        case XmlNodeType.Whitespace:
        case XmlNodeType.SignificantWhitespace:
            if ((text != null && text != baseURI) || (xmlLineInfo != null && xmlLineInfo.HasLineInfo()))
            {
                xNode = new XText(r.Value);
                goto IL_30A;
            }
            xContainer.AddStringSkipNotify(r.Value);
            goto IL_30A;
        case XmlNodeType.CDATA:
            xNode = new XCData(r.Value);
            goto IL_30A;
        case XmlNodeType.EntityReference:
            if (!r.CanResolveEntity)
            {
                goto Block_25;
            }
            r.ResolveEntity();
            goto IL_30A;
        case XmlNodeType.ProcessingInstruction:
            xNode = new XProcessingInstruction(r.Name, r.Value);
            goto IL_30A;
        case XmlNodeType.Comment:
            xNode = new XComment(r.Value);
            goto IL_30A;
        case XmlNodeType.DocumentType:
            xNode = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value, r.DtdInfo);
            goto IL_30A;
        case XmlNodeType.EndElement:
        {
            if (xContainer.content == null)
            {
                xContainer.content = string.Empty;
            }
            XElement xElement2 = xContainer as XElement;
            if (xElement2 != null && xmlLineInfo != null && xmlLineInfo.HasLineInfo())
            {
                xElement2.SetEndElementLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
            }
            if (xContainer == this)
            {
                return;
            }
            if (text != null && xContainer.HasBaseUri)
            {
                text = xContainer.parent.BaseUri;
            }
            xContainer = xContainer.parent;
            goto IL_30A;
        }
        case XmlNodeType.EndEntity:
            goto IL_30A;
        }
        break;
        IL_30A:
        if (xNode != null)
        {
            if (text != null && text != baseURI)
            {
                xNode.SetBaseUri(baseURI);
            }
            if (xmlLineInfo != null && xmlLineInfo.HasLineInfo())
            {
                xNode.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
            }
            xContainer.AddNodeSkipNotify(xNode);
            xNode = null;
        }
        if (!r.Read())
        {
            return;
        }
    }
    goto IL_2E1;
    Block_25:
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnresolvedEntityReference"));
    IL_2E1:
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnexpectedNodeType", new object[]
    {
        r.NodeType
    }));
}

Ответ 2

Он будет анализировать входящий поток (будь то из файла или строки не имеет значения), когда вы вызываете Load(), а затем сохраняете локальный экземпляр документа в памяти. Поскольку источником может быть что угодно (может быть NetworkStream, DataReader, строка, введенная пользователем), он не может вернуться и попытаться прочитать данные снова, поскольку он не будет знать его состояние (потоки закрыты и т.д.)).

Если вам действительно нужна скорость, с другой стороны, XDocument не является быстрым (все, хотя с ним проще работать), так как он должен будет сначала разобрать документ, а затем сохранить его в памяти. Если вы работаете с действительно большими документами, используя подход с System.Xml.XmlReader, как правило, быстрее, поскольку он может читать документ как поток и не работает не нужно ничего сохранять, кроме текущего элемента. Этот показатель показывает некоторые интересные цифры об этом.

Ответ 3

Я не думаю, что он постоянно читает; хорошая идея о методе XDocument.Load заключается в том, что он использует XmlReader для чтения XML в дерево XML. И так как теперь вы создали дерево, которое, скорее всего, хранится в вашей памяти как дерево, оно больше не читает документ постоянно. Он управляет деревом, и поскольку это дерево, все ваши чтения и изменения выполняются намного быстрее. Хотя он не реализует IDisposable, он автоматически удаляется.