2011-02-06 4 views
26

Я проанализировал XML-файл и получил узел, который меня интересует. Как я могу теперь найти номер строки в исходном XML-файле, где этот узел встречается?Получить номер строки из узла xml - java

EDIT: В настоящее время я использую SAXParser для анализа моего XML. Однако я буду доволен решением, использующим любой синтаксический анализатор.

Наряду с узлом у меня также есть выражение XPath для узла.

Мне нужно получить номер строки, потому что я показываю XML-файл в текстовом поле, и вам нужно выделить строку, где произошел узел. Предположим, что XML-файл красиво отформатирован с достаточным разрывом строки.

+3

Разбор с чем? –

ответ

23

Я получил эту работу, следуя этому примеру:

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

Это решение следует методу, предложенному Майклом Кей. Вот как вы его используете:

// 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")); 
    } 
} 

Если вы запустите эту программу, она будет вне положено: «Номер строки: 3»

PositionalXMLReader является слегка модифицированной версией примера, связанного выше.

// 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

Обратите внимание, что это решение отмечает только элементы и игнорирует комментарии и, возможно, CDATA и DTD. Вы можете получить их, выполнив [LexicalHandler] (http://docs.oracle.com/javase/7/docs/api/org/xml/sax/ext/LexicalHandler.html) и вызвав 'setProperty', как указано в javadoc , – thejoshwolfe

8

Если вы используете синтаксический анализатор SAX, тогда номер строки события может быть получен с использованием объекта Locator, который уведомляется ContentHandler через обратный вызов setDocumentLocator(). Это вызывается в начале разбора, и вам нужно сохранить Locator; то после любого события (например, startElement()) вы можете вызвать методы, такие как getLineNumber(), чтобы получить текущую позицию в исходном файле. (После того, как StartElement(), обратный вызов определяется, чтобы дать вам номер строки, на которой «>» из появится начальный тег.)

+0

привет, могу ли я настроить процессор XSLT для саксона (любая версия), чтобы он использовал это как конкретный синтаксический анализатор xml? Я только нашел параметр -x использовать собственный синтаксический анализатор SAX. –

+0

У Saxon есть параметр конфигурации -l или FeatureKeys.LINE_NUMBERING, который заставит его собирать информацию о номере линии, предоставленную синтаксическим анализатором XML, и сохранить его в построенном дереве. Затем он доступен с использованием функции расширения saxon: line-number(). –

+0

спасибо за ответ. Я знаю функцию saxon: номер строки. Мне жаль, что я не был достаточно точным! Ответ priomsrb запускает меня, чтобы модифицировать его PositionalXMLReader, чтобы добавить больше пользовательских данных в узлы. Я нашел функцию saxon: getUserData (только для версий <7.4?) и задавался вопросом, могу ли я использовать это, чтобы получить больше информации о узлах непосредственно в XSLT. (например, номер последней строки/столбца узла.) –

-2

Обратите внимание, что в соответствии со спецификацией (от Locator.getLineNumber()) метод возвращает номер строки, где заканчивается SAX-событий!

В случае "StartElement()" это означает, что:

Здесь номер строки для элемента является :

<Element></Element> 

Здесь номер строки для элемента является :

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

Здравствуйте @hhaehle. Добро пожаловать в SO. Это полезная информация, но, вероятно, ее следует добавить в комментарий, поскольку она не отвечает на исходный вопрос. Вы можете узнать больше о комментариях [здесь] (https://stackoverflow.com/help/privileges/comment). – Chic

Смежные вопросы