从XmlReader获取当前位置

dmo*_*dmo 21 .net xmlreader

有没有办法在XmlReader检查的节点流中获取当前位置?

我想使用XmlReader来解析文档并保存某些元素的位置,以便我以后可以查找它们.

附录:

我正在通过WPF控件生成Xaml.Xaml不应经常更改.在Xaml中有占位符,我需要替换项​​目,有时循环.我认为在代码中而不是转换可能更容易(我可能错了).我的想法是将其解析为需要替换的简单数据结构及其位置,然后使用StringBuilder通过复制xaml字符串中的块来生成最终输出.

小智 9

正如Jon Skeet所说,XmlTextReader实现IXmlLineInfo但是XmlTextReader从那时起就被弃用.NET 2.0了,问题只是关于XmlReader.我找到了这个解决方案

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;
}
Run Code Online (Sandbox Code Playgroud)

PS针对.NET Compact Framework 3.5进行了测试,但也应该适用于其他人.


Jon*_*eet 8

只是在它出现之前提出一个建议:你可以保留对你传入的基础流的引用XmlReader,并记下它的位置 - 但这会给你错误的结果,因为读者几乎肯定会缓冲它的输入(即它会读取前1024个字符或其他任何内容 - 因此您的第一个节点可能"显示"为字符1024).

如果使用XmlTextReader,而不只是XmlReader,然后实现IXmlLineInfo,这意味着你可以要求LineNumberLinePosition在任何时候-就是对你不够好?(HasLineInfo()诚然,你应该先检查一下.)

编辑:我刚刚注意到你希望以后能够找到那个位置......在这种情况下,行信息可能不会非常有用.它非常适合在文本编辑器中查找内容,但对于移动文件指针却不是那么好.你能提供一些关于你想做什么的更多信息吗?可能有更好的方法来解决问题.


G-M*_*Mac 8

我已经为此开发了一个解决方案,虽然它可能无法在每个场景中使用并且使用针对.NET Framework类的私有成员的反射,但我能够XmlReader使用下面显示的扩展方法计算正确的位置.

XmlReader必须StreamReader使用底层创建FileStream(我没有尝试过其他的Streams,只要他们报告他们的位置,他们也可以工作).

我在这里发布了详细信息:http://gmac.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
}
Run Code Online (Sandbox Code Playgroud)