2010-01-29 20 views
21

क्या XmlReader द्वारा परीक्षा के तहत नोड की धारा में वर्तमान स्थिति प्राप्त करने का कोई तरीका है?XmlReader से वर्तमान स्थिति प्राप्त करना

मैं दस्तावेज़ को पार्स करने के लिए XmlReader का उपयोग करना चाहता हूं और कुछ तत्वों की स्थिति को सहेजना चाहता हूं ताकि मैं उन्हें बाद में खोज सकूं।

परिशिष्ट:

मैं एक WPF नियंत्रण द्वारा उत्पन्न Xaml हो रही है। Xaml अक्सर बदलना नहीं चाहिए। Xaml में प्लेसहोल्डर्स हैं जहां मुझे वस्तुओं को बदलने की ज़रूरत है, कभी-कभी लूपिंग। मैंने सोचा कि ट्रांसफॉर्म के बजाय कोड में करना आसान हो सकता है (मैं इसके बारे में गलत हो सकता हूं)। मेरा विचार यह था कि इसे बदलने की जरूरतों और जहां यह है, की एक साधारण डेटा संरचना को पार्स करना था, फिर xaml स्ट्रिंग से भाग कॉपी करके अंतिम आउटपुट बनाने के लिए स्ट्रिंगबिल्डर का उपयोग करें।

+1

क्या आपको इसके साथ कोई सफलता मिली है? मैं कुछ ऐसा करना चाहता हूं (कुछ तत्वों की धारा ऑफ़सेट की गणना करें, फिर बाद में उस ऑफसेट और पार्स की तलाश करें), और अब तक मैं सबसे अच्छा देख सकता हूं कि ऑफ़सेट की गणना करने के लिए फ़ाइल को दो बार पार्स करना है। – Rob

+0

@ रोब नोप। मैं प्रत्येक बार दस्तावेज़ को संसाधित करने के लिए XmlReader/XmlWriter का उपयोग कर समाप्त हुआ। वे मेरे उद्देश्य के लिए पर्याप्त तेज़ हैं कि मैं इस अनुकूलन को छोड़ सकता हूं। – dmo

उत्तर

8

बस एक सुझाव बंद सिर से पहले ही बना दिया है: आप अंतर्निहित धारा आप XmlReader में पारित करने के लिए एक संदर्भ रह सके, और अपनी स्थिति को नोट कर लें - लेकिन है कि आप गलत परिणाम देगा, पाठक के रूप में लगभग निश्चित रूप से इसके इनपुट को बफर कर देगा (यानी यह पहले 1024 वर्ण या जो कुछ भी पढ़ेगा - इसलिए आपका पहला नोड चरित्र 1024 पर "प्रकट" हो सकता है)।

तुम सिर्फ XmlReader के बजाय XmlTextReader का उपयोग करते हैं, तो उस IXmlLineInfo लागू करता है, जिसका अर्थ है आप LineNumber और LinePosition के लिए किसी भी समय पूछ सकते हैं - आप के लिए पर्याप्त है कि अच्छा है? (आपको पहले HasLineInfo() पहले स्वीकार्य रूप से जांचना चाहिए।)

संपादित करें: मैंने अभी देखा है कि आप बाद में उस स्थिति की तलाश में सक्षम होना चाहते हैं ... उस स्थिति में लाइन जानकारी बहुत उपयोगी नहीं हो सकती है। एक टेक्स्ट एडिटर में कुछ ढूंढना बहुत अच्छा है, लेकिन फाइल पॉइंटर को स्थानांतरित करने के लिए इतना अच्छा नहीं है। क्या आप जो कुछ करने की कोशिश कर रहे हैं उसके बारे में कुछ और जानकारी दे सकते हैं? समस्या का सामना करने का एक बेहतर तरीका हो सकता है।

+0

मेरे प्रोटोटाइप के लिए मैं एक लेखक को एक्सएमएल लिख रहा था, उसे फ्लश कर रहा था, और उस स्ट्रीम से लंबाई प्राप्त कर रहा था। यह ठीक काम करता है, लेकिन मैं कुछ क्लीनर और कम स्मृति गहन करने के लिए जाना चाहता हूँ। सूचक के लिए धन्यवाद। – dmo

+0

ऐसा लगता है कि XmlTextReader IXmlLineInfo लागू करता है। – dmo

+0

@Downvoter: टिप्पणी करने की देखभाल? –

3

मुझे एक ही समस्या है और स्पष्ट रूप से कोई आसान समाधान नहीं है।

तो मैं दो केवल पढ़ने के लिए FileStream में हेरफेर करने का फैसला किया: XmlReader के लिए एक, प्रत्येक पंक्ति की स्थिति प्राप्त करने के लिए अन्य:

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; 
     } 
    }    
} 

यह नहीं यह कर, क्योंकि यह दो का उपयोग करता है का सबसे अच्छा तरीका है पाठकों। इससे बचने के लिए, हम XmlReader और लाइन काउंटर के बीच साझा किए गए एक नए फ़ाइल रीडर को फिर से लिख सकते हैं। लेकिन यह आपको बस उस लाइन की ऑफसेट देता है जिसमें आप रुचि रखते हैं। टैग की सटीक ऑफसेट प्राप्त करने के लिए, हमें लाइनपॉजिशन का उपयोग करना चाहिए, लेकिन यह एन्कोडिंग के कारण मुश्किल हो सकता है।

8

मैंने इसके लिए एक समाधान पर काम किया है, और यह हर परिदृश्य में काम नहीं कर सकता है और .NET Framework कक्षाओं के निजी सदस्यों के प्रति प्रतिबिंब का उपयोग करता है, मैं दिखाए गए विस्तार विधि के साथ XmlReader की सही स्थिति की गणना करने में सक्षम हूं नीचे।

आपका XmlReader एक StreamReader का उपयोग कर एक अंतर्निहित FileStream (मैं अन्य Streams प्रयास नहीं किया है, और वे के रूप में अच्छी इतने लंबे समय है कि वे अपने स्थान की रिपोर्ट के रूप में काम कर सकते हैं) से बनाया जाना चाहिए। यहाँ

मैं पोस्ट किया है विवरण: 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 
} 
+0

प्रीमेबल आकार के प्रतिबिंब का उपयोग करने के बजाय, आप इसे अधिक पोर्टेबल और भविष्य के सबूत तरीके से कर सकते हैं: sr। CurrentEncoding.GetPreamble()। लंबाई। इसके अलावा, अगर कहा गया तरीका लंबे समय तक लौटने जा रहा है, तो क्या मैं आपको ऐरे लौटने की सलाह दे सकता हूं। लम्बाईथेंथ या बस एक इंट लौटने के साथ चिपके रहें? – kornman00

+0

असल में, ऐसा लगता है कि इन गुणों में से 32 बिट बिट्स .NET – kornman00

+0

में हैं जहां आप जांचते हैं: (streamReaderBufferLength == DefaultStreamReaderBufferSize? DefaultStreamReaderBufferSize: 0) - संभवतः ऐसा इसलिए है क्योंकि स्ट्रीमreader misreports (शून्य के रूप में)) डेटा के अंतिम ब्लॉक के लिए StreamReader बफर स्थिति? मैंने पाया कि sr.EndOfStream की जांच कर रहा है और, यदि सही है, तो लंबाई को स्थिति में सेट करना (अनिवार्य रूप से एक ही तर्क) - असामान्य रूप से, वास्तव में, sr.EndOfStream प्रॉपर्टी को पुनर्प्राप्त करने के माध्यम से स्थिति "निश्चित" स्थिति को पुनर्प्राप्त करना। हो हम बहुत बढ़िया समाधान बीटीडब्ल्यू। – GHC

2

धन्यवाद ज्योफ।यह विंडोज 7 पर पूरी तरह से काम करता है। लेकिन किसी भी तरह से mscorlib.dll के विंडोज सर्वर 2003 पर .NET 4 संस्करण के साथ, मुझे काम करने के लिए निम्नलिखित 2 फ़ंक्शन बदलना पड़ा।

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; 
    } 

इसके अलावा GetPosition विधि में अंतर्निहित स्ट्रीमर रीडर पॉइंटर को अग्रिम करने के लिए peek होना चाहिए।

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; 
    } 
+0

अच्छी जानकारी। और वहां आपके पास पूर्ण प्रदर्शन पर निजी ढांचे के सदस्यों तक पहुंचने के खतरे हैं ... :) –

7

जॉन स्कीट कहते हैं, 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; 
} 

पीएस .NET कॉम्पैक्ट फ्रेमवर्क 3.5 के लिए परीक्षण किया गया, लेकिन दूसरों के लिए भी काम करना चाहिए।

+1

सही समाधान; दूसरों को बहुत गड़बड़ लगती है। शायद 2010 में वापस, यह उपलब्ध नहीं था ?? –

संबंधित मुद्दे