Compartir a través de


Cómo transmitir por secuencias fragmentos XML con acceso a la información de encabezado

Actualización: November 2007

A veces tiene que leer arbitrariamente los archivos XML de gran tamaño y escribir la aplicación para que la superficie en memoria de la aplicación sea predecible. Si intenta rellenar un árbol XML con un archivo XML de gran tamaño, su utilización de memoria será proporcional al tamaño del archivo (es decir, excesivo). Por consiguiente, debe utilizar en su lugar una técnica de transmisión por secuencias.

Una opción consiste en escribir la aplicación usando XmlReader. No obstante, quizás desea usar LINQ para consultar el árbol XML. En ese caso, puede escribir su propio método de eje personalizado. Para obtener más información, vea Cómo escribir un método de eje LINQ to XML.

Para escribir su propio método de eje, debe escribir un pequeño método que utiliza los nodos XmlReader para leer hasta que llega a uno de los nodos en el que está interesado. A continuación el método llama a ReadFrom, que lee de XmlReader y crea una instancia de un fragmento XML. A continuación ofrece cada fragmento a través de yield return al método que está enumerando su método de eje personalizado. A continuación puede escribir las consultas de LINQ en su método de eje personalizado.

Las técnicas de transmisión por secuencias se aplican mejor en situaciones en las que el documento de origen sólo se debe procesar una vez y se pueden procesar los elementos en el orden del documento. Ciertos operadores de consulta estándar, como OrderBy, recorren en iteración su origen, recaban todos los datos, los ordenan y finalmente producen el primer elemento de la secuencia. Tenga en cuenta que si utiliza un operador de consulta que materializa su origen antes de producir el primer elemento, no retendrá una superficie de memoria pequeña.

Ejemplo

A veces el problema se pone un poco más interesante. En el siguiente documento XML, el usuario de su método de eje personalizado también tiene que conocer el nombre del cliente al que pertenece cada elemento.

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Customer>
    <Name>A. Datum Corporation</Name>
    <Item>
      <Key>0001</Key>
    </Item>
    <Item>
      <Key>0002</Key>
    </Item>
    <Item>
      <Key>0003</Key>
    </Item>
    <Item>
      <Key>0004</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Fabrikam, Inc.</Name>
    <Item>
      <Key>0005</Key>
    </Item>
    <Item>
      <Key>0006</Key>
    </Item>
    <Item>
      <Key>0007</Key>
    </Item>
    <Item>
      <Key>0008</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Southridge Video</Name>
    <Item>
      <Key>0009</Key>
    </Item>
    <Item>
      <Key>0010</Key>
    </Item>
  </Customer>
</Root>

El enfoque que toma este ejemplo consiste en consultar también esta información de encabezado, guardar la información de encabezado y después crear un pequeño árbol XML que contiene la información de encabezado y el detalle que está enumerando. El método de eje ofrece este nuevo y pequeño árbol XML. La consulta tiene acceso a la información de encabezado así como a la información detallada.

Este enfoque ocupa una pequeña superficie de memoria. Como se ofrecen todos los fragmentos XML detallados, no se guardan referencias al fragmento anterior y está disponible para la recolección de elementos no utilizados. Tenga en cuenta que esta técnica crea muchos objetos de vida corta en el montón.

En el siguiente ejemplo se muestra cómo implementar y utilizar un método de eje personalizado que transmite por secuencias fragmentos XML del archivo especificado por la URI. Este eje personalizado ha sido escrito específicamente para que espere un documento que tiene los elementos Customer, Name, y Item, y que esos elementos se ordenarán como en el documento Source.xml anterior. Se trata de una implementación simplista. Una implementación más sólida estaría preparada para analizar un documento no válido.

Nota:

En el ejemplo de código siguiente se utiliza la construcción yield return de C#. Puesto que no hay ninguna característica equivalente en Visual Basic 2008, este ejemplo sólo se proporciona en C#.

static IEnumerable<XElement> StreamCustomerItem(string uri)
{
    using (XmlReader reader = XmlReader.Create(uri))
    {
        XElement name = null;
        XElement item = null;

        reader.MoveToContent();

        // Parse the file, save header information when encountered, and yield the
        // Item XElement objects as they are created.

        // loop through Customer elements
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element
                && reader.Name == "Customer")
            {
                // move to Name element
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element &&
                        reader.Name == "Name")
                    {
                        name = XElement.ReadFrom(reader) as XElement;
                        break;
                    }
                }
                
                // loop through Item elements
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;
                    if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == "Item")
                    {
                        item = XElement.ReadFrom(reader) as XElement;
                        if (item != null) {
                            XElement tempRoot = new XElement("Root",
                                new XElement(name)
                            );
                            tempRoot.Add(item);
                            yield return item;
                        }
                    }
                }
            }
        }
    }
}

static void Main(string[] args)
{
    XElement xmlTree = new XElement("Root",
        from el in StreamCustomerItem("Source.xml")
        where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7
        select new XElement("Item",
            new XElement("Customer", (string)el.Parent.Element("Name")),
            new XElement(el.Element("Key"))
        )
    );
    Console.WriteLine(xmlTree);
}

Este código genera el siguiente resultado:

<Root>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0003</Key>
  </Item>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0004</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0005</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0006</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0007</Key>
  </Item>
</Root>

Vea también

Conceptos

Programación avanzada de LINQ to XML