¿La forma más rápida de agregar un nuevo nodo al final de un xml?

Tengo un gran archivo xml (aproximadamente 10 MB) siguiendo una estructura simple:

 ....... ....... ....... ....... .......  

Mi necesidad es escribir agregar un nuevo nodo al final antes de la etiqueta . ¿Cuál es la forma más rápida de lograr esto en .net?

Necesita usar la técnica de inclusión XML.

Su error.xml (no cambia, solo un stub. Usado por los analizadores XML para leer):

   ]>  &logrows;  

Su archivo errorrows.txt (cambios, el analizador xml no lo entiende):

 .... .... .... 

Luego, para agregar una entrada a errorrows.txt:

 using (StreamWriter sw = File.AppendText("logerrors.txt")) { XmlTextWriter xtw = new XmlTextWriter(sw); xtw.WriteStartElement("Error"); // ... write error messge here xtw.Close(); } 

O incluso puede usar .NET 3.5 XElement y anexar el texto a StreamWriter :

 using (StreamWriter sw = File.AppendText("logerrors.txt")) { XElement element = new XElement("Error"); // ... write error messge here sw.WriteLine(element.ToString()); } 

Ver también el artículo de Microsoft Técnicas eficientes para modificar archivos XML grandes

Primero, descalificaría a System.Xml.XmlDocument porque es un DOM que requiere analizar y construir todo el árbol en la memoria antes de que pueda agregarse. Esto significa que sus 10 MB de texto tendrán más de 10 MB en la memoria. Esto significa que es “intensivo de memoria” y “consume mucho tiempo”.

En segundo lugar, descalificaría System.Xml.XmlReader porque requiere analizar primero el archivo completo antes de que pueda llegar al punto en el que puede agregarlo. Tendría que copiar el XmlReader en un XmlWriter ya que no puede modificarlo. Esto requiere la duplicación de su XML en la memoria antes de poder agregarlo.

La solución más rápida para XmlDocument y XmlReader sería la manipulación de cadenas (que tiene sus propios problemas de memoria):

 string xml = @"..."; int idx = xml.LastIndexOf(""); xml = xml.Substring(0, idx) + "new error"; 

Corte la etiqueta final, agregue el nuevo error y vuelva a agregar la etiqueta final.

Supongo que podría volverse loco con esto y truncar su archivo por 9 caracteres y anexarlo. No tendría que leer en el archivo y dejaría que el SO optimice la carga de la página (solo tendría que cargarse en el último bloque o algo así).

 System.IO.FileStream fs = System.IO.File.Open("log.xml", System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite); fs.Seek(-("".Length), System.IO.SeekOrigin.End); fs.Write("new error"); fs.Close(); 

Esto generará un problema si su archivo está vacío o contiene solo “ “, los cuales pueden manejarse fácilmente al verificar la longitud.

La manera más rápida probablemente sea un acceso directo a archivos.

 using (StreamWriter file = File.AppendText("my.log")) { file.BaseStream.Seek(-"".Length, SeekOrigin.End); file.Write(" New error message."); } 

Pero pierde todas las características agradables de XML y puede corromper el archivo fácilmente.

Yo usaría XmlDocument o XDocument para cargar su archivo y luego lo manipularía en consecuencia.

Luego vería la posibilidad de almacenar este XmlDocument en la memoria para que pueda acceder al archivo rápidamente.

¿Para qué necesitas la velocidad? ¿Ya tiene un cuello de botella de rendimiento o está esperando uno?

Probar esto:

  var doc = new XmlDocument(); doc.LoadXml("This is my first error"); XmlNode root = doc.DocumentElement; //Create a new node. XmlElement elem = doc.CreateElement("error"); elem.InnerText = "This is my error"; //Add the node to the document. if (root != null) root.AppendChild(elem); doc.Save(Console.Out); Console.ReadLine(); 

¿Cómo se representa tu archivo XML en el código? ¿Usas las clases System.XML? En este caso, podría usar XMLDocument.AppendChild.

He aquí cómo hacerlo en C, .NET debería ser similar.

El juego consiste simplemente en saltar al final del archivo, saltar la etiqueta, agregar la nueva línea de error y escribir una nueva etiqueta.

 #include  #include  #include  int main(int argc, char** argv) { FILE *f; // Open the file f = fopen("log.xml", "r+"); // Small buffer to determine length of \n (1 on Unix, 2 on PC) // You could always simply hard code this if you don't plan on // porting to Unix. char nlbuf[10]; sprintf(nlbuf, "\n"); // How long is our end tag? long offset = strlen(""); // Add in an \n char. offset += strlen(nlbuf); // Seek to the END OF FILE, and then GO BACK the end tag and newline // so we use a NEGATIVE offset. fseek(f, offset * -1, SEEK_END); // Print out your new error line fprintf(f, "New error line\n"); // Print out new ending tag. fprintf(f, "\n"); // Close and you're done fclose(f); } 

Es probable que el método más rápido sea leer en el archivo usando un XmlReader y simplemente replicar cada nodo leído en una nueva stream usando XmlWriter Cuando llegas al punto en el que encuentras la etiqueta de cierre , solo necesitas genere su elemento adicional antes de continuar con el ciclo “leer y duplicar”. De esta forma, inevitablemente va a ser más difícil que leer todo el documento en el DOM (clase XmlDocument ), pero para archivos XML grandes, mucho más rápido. Es cierto que usar StreamReader / StreamWriter sería aún más rápido, pero es bastante horrible trabajar con código.

Usar técnicas basadas en cadenas (como buscar hasta el final del archivo y luego retroceder en la longitud de la etiqueta de cierre) es vulnerable a variaciones inesperadas pero perfectamente legales en la estructura del documento.

El documento podría terminar con cualquier cantidad de espacios en blanco, para elegir el problema más probable que encuentre. También podría terminar con cualquier cantidad de comentarios o instrucciones de procesamiento. ¿Y qué pasa si el elemento de nivel superior no se llama Error ?

Y aquí hay una situación que al usar la manipulación de cadenas falla completamente para detectar:

  ...  

Si usa un XmlReader para procesar el XML, aunque puede no ser tan rápido como buscar EOF, también le permitirá manejar todas estas posibles condiciones de excepción.