leer from ejemplo .net xmlreader

.net - from - obtener la posición actual de un XmlReader



xmlreader java (5)

¿Hay alguna forma de obtener la posición actual en el flujo del nodo que está examinando el XmlReader?

Me gustaría usar XmlReader para analizar un documento y guardar la posición de ciertos elementos para poder buscarlos más tarde.

Apéndice:

Estoy obteniendo Xaml generado por un control WPF. El Xaml no debe cambiar con frecuencia. Hay marcadores de posición en el Xaml donde necesito reemplazar elementos, a veces en bucle. Pensé que podría ser más fácil hacer código en lugar de una transformación (podría estar equivocado al respecto). Mi idea fue analizarla en una estructura de datos simple de lo que se debe reemplazar y dónde está, luego usar un StringBuilder para producir la salida final al copiar fragmentos de la cadena xaml.


Como dice Jon Skeet, XmlTextReader implementa IXmlLineInfo pero XmlTextReader está en desuso desde .NET 2.0 y la pregunta es solo sobre XmlReader . Encontré esta solución:

XmlReader xr = XmlReader.Create( // MSDN recommends to use Create() instead of ctor() new StringReader("<some><xml><string><data>"), someSettings // furthermore, can''t set XmlSettings on XmlTextReader ); IXmlLineInfo xli = (IXmlLineInfo)xr; while (xr.Read()) { // ... some read actions ... // current position in StringReader can be accessed through int line = xli.LineNumber; int pos = xli.LinePosition; }

PS probado para .NET Compact Framework 3.5, pero también debería funcionar para otros.


Gracias Geoff por la respuesta. Funcionó perfectamente en Windows 7. Pero de alguna manera con la versión .net 4 en Windows Server 2003 de mscorlib.dll, tuve que cambiar las siguientes 2 funciones para funcionar.

private long GetStreamReaderBufferLength(StreamReader sr) { FieldInfo _streamReaderByteLenField = sr.GetType() .GetField("charLen", BindingFlags.Instance | BindingFlags.NonPublic); var fValue = (int)_streamReaderByteLenField.GetValue(sr); return fValue; } private int GetStreamReaderBufferPos(StreamReader sr) { FieldInfo _streamReaderBufferPositionField = sr.GetType() .GetField("charPos", BindingFlags.Instance | BindingFlags.NonPublic); int fvalue = (int)_streamReaderBufferPositionField.GetValue(sr); return fvalue; }

También se debe echar un vistazo al método subyacenteStreamReader en GetPosition para avanzar el puntero.

private long GetPosition(XmlReader xr, StreamReader underlyingStreamReader) { long pos = -1; while (pos < 0) { // Get the position of the FileStream underlyingStreamReader.Peek(); long fileStreamPos = underlyingStreamReader.BaseStream.Position; // long fileStreamPos = GetStreamReaderBasePosition(underlyingStreamReader); // Get current XmlReader state long xmlReaderBufferLength = GetXmlReaderBufferLength(xr); long xmlReaderBufferPos = GetXmlReaderBufferPosition(xr); // Get current StreamReader state long streamReaderBufferLength = GetStreamReaderBufferLength(underlyingStreamReader); long streamReaderBufferPos = GetStreamReaderBufferPos(underlyingStreamReader); long preambleSize = GetStreamReaderPreambleSize(underlyingStreamReader); // Calculate the actual file position pos = fileStreamPos - (streamReaderBufferLength == DefaultStreamReaderBufferSize ? DefaultStreamReaderBufferSize : 0) - xmlReaderBufferLength + xmlReaderBufferPos + streamReaderBufferPos;// -preambleSize; } return pos; }


He trabajado en una solución para esto, y aunque puede que no funcione en todos los escenarios y use la reflexión en contra de los miembros privados de las clases de .NET Framework, puedo calcular la posición correcta del XmlReader con el método de extensión que se muestra a continuación.

Su XmlReader debe crearse a partir de un StreamReader utilizando un FileStream subyacente (no he probado otros Streams y pueden funcionar tan bien siempre que informen su posición).

He publicado detalles aquí: http://g-m-a-c.blogspot.com/2013/11/determine-exact-position-of-xmlreader.html

public static class XmlReaderExtensions { private const long DefaultStreamReaderBufferSize = 1024; public static long GetPosition(this XmlReader xr, StreamReader underlyingStreamReader) { // Get the position of the FileStream long fileStreamPos = underlyingStreamReader.BaseStream.Position; // Get current XmlReader state long xmlReaderBufferLength = GetXmlReaderBufferLength(xr); long xmlReaderBufferPos = GetXmlReaderBufferPosition(xr); // Get current StreamReader state long streamReaderBufferLength = GetStreamReaderBufferLength(underlyingStreamReader); int streamReaderBufferPos = GetStreamReaderBufferPos(underlyingStreamReader); long preambleSize = GetStreamReaderPreambleSize(underlyingStreamReader); // Calculate the actual file position long pos = fileStreamPos - (streamReaderBufferLength == DefaultStreamReaderBufferSize ? DefaultStreamReaderBufferSize : 0) - xmlReaderBufferLength + xmlReaderBufferPos + streamReaderBufferPos - preambleSize; return pos; } #region Supporting methods private static PropertyInfo _xmlReaderBufferSizeProperty; private static long GetXmlReaderBufferLength(XmlReader xr) { if (_xmlReaderBufferSizeProperty == null) { _xmlReaderBufferSizeProperty = xr.GetType() .GetProperty("DtdParserProxy_ParsingBufferLength", BindingFlags.Instance | BindingFlags.NonPublic); } return (int) _xmlReaderBufferSizeProperty.GetValue(xr); } private static PropertyInfo _xmlReaderBufferPositionProperty; private static int GetXmlReaderBufferPosition(XmlReader xr) { if (_xmlReaderBufferPositionProperty == null) { _xmlReaderBufferPositionProperty = xr.GetType() .GetProperty("DtdParserProxy_CurrentPosition", BindingFlags.Instance | BindingFlags.NonPublic); } return (int) _xmlReaderBufferPositionProperty.GetValue(xr); } private static PropertyInfo _streamReaderPreambleProperty; private static long GetStreamReaderPreambleSize(StreamReader sr) { if (_streamReaderPreambleProperty == null) { _streamReaderPreambleProperty = sr.GetType() .GetProperty("Preamble_Prop", BindingFlags.Instance | BindingFlags.NonPublic); } return ((byte[]) _streamReaderPreambleProperty.GetValue(sr)).Length; } private static PropertyInfo _streamReaderByteLenProperty; private static long GetStreamReaderBufferLength(StreamReader sr) { if (_streamReaderByteLenProperty == null) { _streamReaderByteLenProperty = sr.GetType() .GetProperty("ByteLen_Prop", BindingFlags.Instance | BindingFlags.NonPublic); } return (int) _streamReaderByteLenProperty.GetValue(sr); } private static PropertyInfo _streamReaderBufferPositionProperty; private static int GetStreamReaderBufferPos(StreamReader sr) { if (_streamReaderBufferPositionProperty == null) { _streamReaderBufferPositionProperty = sr.GetType() .GetProperty("CharPos_Prop", BindingFlags.Instance | BindingFlags.NonPublic); } return (int) _streamReaderBufferPositionProperty.GetValue(sr); } #endregion }


Tengo el mismo problema y aparentemente no hay una solución simple.

Así que decidí manipular dos FileStream de solo lectura: uno para XmlReader, el otro para obtener la posición de cada línea:

private void ReadXmlWithLineOffset() { string malformedXml = "<test>/n<test2>/r <test3><test4>/r/n<test5>Thi is/r/ra/ntest</test5></test4></test3></test2>"; string fileName = "test.xml"; File.WriteAllText(fileName, malformedXml); XmlTextReader xr = new XmlTextReader(new FileStream(fileName, FileMode.Open, FileAccess.Read)); FileStream fs2 = new FileStream(fileName, FileMode.Open, FileAccess.Read); try { int currentLine = 1; while(xr.Read()) { if (!string.IsNullOrEmpty(xr.Name)) { for (;currentLine < xr.LineNumber; currentLine++) ReadLine(fs2); Console.WriteLine("{0} : LineNum={1}, FileOffset={2}", xr.Name, xr.LineNumber, fs2.Position); } } } catch (Exception ex) { Console.WriteLine("Exception : " + ex.Message); } finally { xr.Close(); fs2.Dispose(); } } private void ReadLine(FileStream fs) { int b; while ((b = fs.ReadByte()) >= 0) { if (b == 10) // /n return; if (b == 13) // /r { if (fs.ReadByte() != 10) // if not /r/n, go back one byte fs.Seek(-1, SeekOrigin.Current); return; } } }

Esta no es la mejor manera de hacerlo porque utiliza dos lectores. Para evitar esto, podríamos reescribir un nuevo FileReader compartido entre XmlReader y el contador de líneas. Pero simplemente le da el desplazamiento de la línea en la que está interesado. Para obtener el desplazamiento exacto de la etiqueta, deberíamos usar LinePosition, pero esto puede ser complicado debido a la codificación.


Solo para evitar una sugerencia antes de que se haga: puede mantener una referencia a la transmisión subyacente que pasa a XmlReader y tomar nota de su posición, pero eso le dará los resultados incorrectos, ya que el lector casi con seguridad almacenará su contenido. entrada (es decir, leerá los primeros 1024 caracteres o lo que sea, por lo que su primer nodo podría "aparecer" en el carácter 1024).

Si usa XmlTextReader lugar de XmlReader , entonces implementa IXmlLineInfo , lo que significa que puede solicitar el LineNumber y la LinePosition en cualquier momento. ¿Es eso lo suficientemente bueno para usted? (Probablemente debería marcar HasLineInfo() primero, por HasLineInfo() .)

EDIT: Acabo de notar que desea poder buscar esa posición más adelante ... en ese caso, la información de la línea puede no ser muy útil. Es genial para encontrar algo en un editor de texto, pero no tanto para mover el puntero de un archivo. ¿Podría dar más información sobre lo que está tratando de hacer? Puede haber una mejor manera de abordar el problema.