2011-02-06 28 views
26

私はXMLファイルを解析し、興味のあるノードを取得しました。このノードが存在するソースXMLファイルの行番号を今すぐ見つけることができますか?xmlノードから行番号を取得する - Java

EDIT: 現在、私はXMLを解析するためにSAXParserを使用しています。しかし、私は任意のパーサーを使用したソリューションに満足しています。

ノードに加えて、ノードのXPath式もあります。

XMLファイルをテキストボックスに表示しているため、ノードが発生した行を強調表示する必要があるため、行番号を取得する必要があります。十分な改行でXMLファイルがうまくフォーマットされていると仮定してください。

+3

何と解析? –

答えて

23

私はこの例に従うことによって、この作業を持っている:

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

このソリューションは、Michael Kayの提案する方法に従います。ここでは、あなたがそれを使用する方法です:

あなたがこのプログラムを実行すると、それは出します
// 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は、上記のリンクの例を少し変更したバージョンです。 (Locator.getLineNumber()の)仕様に従った方法は、SAXイベントが終了する行番号を返す

// 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)を実装し、javadocの指示に従って 'setProperty'を呼び出すことでそれらを取得できます。 – thejoshwolfe

8

SAXパーサーを使用している場合、ロケータオブジェクトを使用してイベントの行番号を取得できます。この行番号は、setDocumentLocator()コールバックを介してContentHandlerに通知されます。これは解析の開始時に呼び出され、ロケータを保存する必要があります。任意のイベント(startElement()など)の後に、getLineNumber()などのメソッドを呼び出して、ソースファイル内の現在の位置を取得できます。 (のstartElement()した後、コールバックはあなたにタグが表示され、スタートの、その上に「>」の行番号を与えるように定義されている。)

+0

こんにちは、これを特定のxmlパーサとして使用するsaxon XSLTプロセッサ(任意のバージョン)を設定できますか?私はパラメータ-xが独自のSAXパーサを使うことしか見つけませんでした。 –

+0

Saxonには、-lまたはFeatureKeys.LINE_NUMBERINGという構成オプションがあり、XMLパーサから提供された行番号情報を収集し、構築されたツリーに保持します。次に、saxon:line-number()拡張関数を使用してアクセスできます。 –

+0

答えに感謝します。私はsaxon:line-number関数を知っています。申し訳ありません、私は十分に正確ではありませんでした! priomsrbの答えは彼のPositionalXMLReaderを変更してノードにユーザーデータを追加するようにしました。私はsaxon:getUserData関数を見つけました(バージョン<7.4?)、それをノードの詳細をXSLTに直接取得するために使用できるかどうか疑問に思っていました。 (例えば、ノードの最後の行/列番号) –

-2

注意! "のstartElement()" この手段の場合

ためここ

を行番号の要素がある:

<Element></Element> 

ここ要素の行番号あります3

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

こんにちは@hhaehle。ようこそ。これは参考になる情報ですが、元の質問には答えられないため、コメントに記入してください。コメントの詳細については、こちらをご覧ください(https://stackoverflow.com/help/privileges/comment)。 – Chic

関連する問題