2011-02-06 16 views
26

Przetworzyłem plik XML i uzyskałem węzeł, który mnie interesuje. Jak mogę teraz znaleźć numer wiersza w źródłowym pliku XML, w którym występuje ten węzeł?Uzyskaj numer wiersza z węzła xml - java

EDYTOWANIE: Obecnie używam SAXParser do parsowania mojego XML. Jednak będę zadowolony z rozwiązania przy użyciu dowolnego parsera.

Wraz z węzłem mam również wyrażenie XPath dla węzła.

Potrzebuję uzyskać numer wiersza, ponieważ wyświetlam plik XML w polu tekstowym i muszę podświetlić linię, w której wystąpił węzeł. Załóżmy, że plik XML jest ładnie sformatowany z wystarczającymi podziałami linii.

+3

Parsowane z czym? –

Odpowiedz

23

mam tej pracy, wykonując tym przykładzie:

http://eyalsch.wordpress.com/2010/11/30/xml-dom-2/

To rozwiązanie jest zgodne z metodą sugerowaną przez Michaela Kaya. Oto jak go używać:

// XmlTest.java 

import java.io.ByteArrayInputStream; 
import java.io.InputStream; 

import org.w3c.dom.Document; 
import org.w3c.dom.Node; 

public class XmlTest { 
    public static void main(final String[] args) throws Exception { 

     String xmlString = "<foo>\n" 
         + " <bar>\n" 
         + "  <moo>Hello World!</moo>\n" 
         + " </bar>\n" 
         + "</foo>"; 

     InputStream is = new ByteArrayInputStream(xmlString.getBytes()); 
     Document doc = PositionalXMLReader.readXML(is); 
     is.close(); 

     Node node = doc.getElementsByTagName("moo").item(0); 

     System.out.println("Line number: " + node.getUserData("lineNumber")); 
    } 
} 

Jeśli uruchomić ten program, będzie się umieścić: „Numer linii: 3”

PositionalXMLReader jest nieco zmodyfikowaną wersją przykład połączonego powyżej.

// PositionalXMLReader.java 

import java.io.IOException; 
import java.io.InputStream; 
import java.util.Stack; 

import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.parsers.ParserConfigurationException; 
import javax.xml.parsers.SAXParser; 
import javax.xml.parsers.SAXParserFactory; 

import org.w3c.dom.Document; 
import org.w3c.dom.Element; 
import org.w3c.dom.Node; 
import org.xml.sax.Attributes; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.helpers.DefaultHandler; 

public class PositionalXMLReader { 
    final static String LINE_NUMBER_KEY_NAME = "lineNumber"; 

    public static Document readXML(final InputStream is) throws IOException, SAXException { 
     final Document doc; 
     SAXParser parser; 
     try { 
      final SAXParserFactory factory = SAXParserFactory.newInstance(); 
      parser = factory.newSAXParser(); 
      final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 
      final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 
      doc = docBuilder.newDocument(); 
     } catch (final ParserConfigurationException e) { 
      throw new RuntimeException("Can't create SAX parser/DOM builder.", e); 
     } 

     final Stack<Element> elementStack = new Stack<Element>(); 
     final StringBuilder textBuffer = new StringBuilder(); 
     final DefaultHandler handler = new DefaultHandler() { 
      private Locator locator; 

      @Override 
      public void setDocumentLocator(final Locator locator) { 
       this.locator = locator; // Save the locator, so that it can be used later for line tracking when traversing nodes. 
      } 

      @Override 
      public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) 
        throws SAXException { 
       addTextIfNeeded(); 
       final Element el = doc.createElement(qName); 
       for (int i = 0; i < attributes.getLength(); i++) { 
        el.setAttribute(attributes.getQName(i), attributes.getValue(i)); 
       } 
       el.setUserData(LINE_NUMBER_KEY_NAME, String.valueOf(this.locator.getLineNumber()), null); 
       elementStack.push(el); 
      } 

      @Override 
      public void endElement(final String uri, final String localName, final String qName) { 
       addTextIfNeeded(); 
       final Element closedEl = elementStack.pop(); 
       if (elementStack.isEmpty()) { // Is this the root element? 
        doc.appendChild(closedEl); 
       } else { 
        final Element parentEl = elementStack.peek(); 
        parentEl.appendChild(closedEl); 
       } 
      } 

      @Override 
      public void characters(final char ch[], final int start, final int length) throws SAXException { 
       textBuffer.append(ch, start, length); 
      } 

      // Outputs text accumulated under the current node 
      private void addTextIfNeeded() { 
       if (textBuffer.length() > 0) { 
        final Element el = elementStack.peek(); 
        final Node textNode = doc.createTextNode(textBuffer.toString()); 
        el.appendChild(textNode); 
        textBuffer.delete(0, textBuffer.length()); 
       } 
      } 
     }; 
     parser.parse(is, handler); 

     return doc; 
    } 
} 
+0

Zwróć uwagę, że to rozwiązanie rozpoznaje tylko elementy i ignoruje komentarze, a także CDATA i DTD. Możesz je uzyskać, implementując [LexicalHandler] (http://docs.oracle.com/javase/7/docs/api/org/xml/sax/ext/LexicalHandler.html) i wywołując 'setProperty' zgodnie z instrukcją javadoc . – thejoshwolfe

8

Jeśli korzystasz z parsera SAX, numer linii zdarzenia można uzyskać za pomocą obiektu Locator, który jest przekazywany do ContentHandler za pomocą wywołania zwrotnego setDocumentLocator(). Nazywa się to na początku analizowania i musisz zapisać Lokalizator; następnie po każdym zdarzeniu (takim jak startElement()) można wywoływać metody takie jak getLineNumber() w celu uzyskania aktualnej pozycji w pliku źródłowym. (Po startElement(), wywołanie zwrotne jest zdefiniowane, aby dać Ci numer linii, na której „>” od pojawi się znacznik startu).

+0

witam, czy mogę skonfigurować procesor saxon XSLT (dowolna wersja), który używa go jako określonego parsera xml? Znalazłem tylko parametr -x, aby użyć własnego analizatora składni SAX. –

+0

Saxon ma opcję konfiguracji -l lub FeatureKeys.LINE_NUMBERING, która spowoduje zbieranie informacji o numerze linii dostarczonych przez analizator składni XML i zachowanie go w skonstruowanym drzewie. Jest wtedy dostępny za pomocą funkcji rozszerzenia saxon: line-number(). –

+0

dzięki za odpowiedź. znam sakson: numer linii. Przepraszam, nie byłam wystarczająco precyzyjna! Odpowiedź priomsrb spowodowała, że ​​zmodyfikowałem jego PositionalXMLReader, aby dodać więcej danych użytkownika do węzłów. Znalazłem funkcję saxon: getUserData (tylko dla wersji <7.4?) i zastanawiałem się, czy mógłbym użyć tego, by uzyskać więcej informacji o węzłach bezpośrednio do XSLT. (na przykład ostatni numer wiersza/kolumny węzła). –

-2

Należy zauważyć, że zgodnie ze specyfikacją (z Locator.getLineNumber()) sposób wraca liczbę linii, gdzie kończy się SAX zdarzenia!

W przypadku "startElement()", to znaczy:

Tutaj liczba linii na element jest :

<Element></Element> 

Tutaj liczba linii na element jest :

<Element 
    attribute1="X" 
    attribute2="Y"> 
</Element> 
+0

Witaj @ hhaehle. Witamy w SO. Jest to kilka pomocnych informacji, ale prawdopodobnie należy umieścić je w komentarzu, ponieważ nie odpowiada na oryginalne pytanie. Możesz dowiedzieć się więcej o komentarzach [tutaj] (https://stackoverflow.com/help/privileges/comment). – Chic

Powiązane problemy