2011-02-06 58 views
26

我已經解析了一個XML文件,並獲得了一個我感興趣的節點。我現在如何在此節點出現的源XML文件中找到行號?從xml節點獲取行號 - java

編輯: 目前我正在使用SAXParser來解析我的XML。不過,我會很滿意使用任何解析器的解決方案。

隨着節點,我也有節點的XPath表達式。

我需要獲取行號,因爲我在文本框中顯示XML文件,並且需要突出顯示節點發生的行。假設XML文件格式良好並具有足夠的換行符。

+3

解析了什麼? – 2011-02-06 19:06:12

回答

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是上面鏈接的例子略加修改的版本。

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

如果您使用的是SAX解析器,則可以使用Locator對象獲取事件的行號,該對象通過setDocumentLocator()回調函數通知給ContentHandler。這在解析開始時被調用,並且您需要保存定位器;那麼在任何事件(例如startElement())之後,您可以調用諸如getLineNumber()的方法來獲取源文件中的當前位置。 (的startElement(),回調定義後給你上的「>」出現時,開始標籤的行號。)

+0

你好,我可以配置撒克遜XSLT處理器(任何版本),它將它用作特定的xml解析器嗎?我只找到參數-x來使用自己的SAX解析器。 – 2013-02-09 09:29:40

+0

撒克遜有一個配置選項-l或FeatureKeys.LINE_NUMBERING,它將使它收集由XML解析器提供的行號信息並將其保留在構造的樹中。然後使用saxon:line-number()擴展函數訪問它。 – 2013-02-09 22:07:06

+0

感謝您的回答。我知道撒克遜人:行號功能。我很抱歉,我不夠精確! priomsrb的答案觸發我修改了他的PositionalXMLReader以向節點添加更多用戶數據。我發現了saxon:getUserData函數(僅適用於版本<7.4?),並想知道我是否可以使用它直接將更多關於節點的信息導入到XSLT中。 (例如節點的最後一行/列號)。 – 2013-02-10 21:43:51

-2

注意的是,根據規格(的Locator.getLineNumber())該方法返回,其中SAX-事件結束行號!

在的情況下 「的startElement()」,這意味着:

在這裏,行號爲是:

<Element></Element> 

這裏元素行數:

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