2011-02-06 32 views
26

Tôi đã phân tích cú pháp một tệp XML và đã nhận được Nút mà tôi quan tâm. Làm cách nào tôi có thể tìm số dòng trong tệp XML nguồn nơi nút này xảy ra?Lấy số dòng từ nút xml - java

EDIT: Hiện tại tôi đang sử dụng SAXParser để phân tích cú pháp XML của mình. Tuy nhiên tôi sẽ hài lòng với giải pháp sử dụng bất kỳ trình phân tích cú pháp nào.

Cùng với nút, tôi cũng có biểu thức XPath cho nút.

Tôi cần lấy số dòng vì tôi đang hiển thị tệp XML trong hộp văn bản và cần đánh dấu dòng nơi nút xuất hiện. Giả sử rằng tệp XML được định dạng độc đáo với các ngắt dòng đầy đủ.

+3

Phân tích cú pháp với nội dung gì? –

Trả lời

23

Tôi đã làm việc này bằng cách làm theo ví dụ này:

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

Giải pháp này tuân theo phương pháp do Michael Kay đề xuất. Đây là cách bạn sử dụng nó:

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

Nếu bạn chạy chương trình này, nó sẽ ra đưa: "Số Line: 3"

PositionalXMLReader là một phiên bản sửa đổi một chút trong những ví dụ liên kết ở trên.

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

lưu ý rằng giải pháp này chỉ thông báo các yếu tố, và bỏ qua các ý kiến ​​và cũng có thể CDATA và DTD. Bạn có thể lấy chúng bằng cách triển khai [LexicalHandler] (http://docs.oracle.com/javase/7/docs/api/org/xml/sax/ext/LexicalHandler.html) và gọi 'setProperty' theo hướng dẫn của javadoc . – thejoshwolfe

8

Nếu bạn đang sử dụng trình phân tích cú pháp SAX thì số dòng của sự kiện có thể thu được bằng cách sử dụng đối tượng Locator, được thông báo cho ContentHandler thông qua gọi lại setDocumentLocator(). Điều này được gọi là lúc bắt đầu phân tích cú pháp, và bạn cần phải lưu Locator; sau đó sau bất kỳ sự kiện nào (chẳng hạn như startElement()), bạn có thể gọi các phương thức như getLineNumber() để lấy vị trí hiện tại trong tệp nguồn. (Sau startElement(), gọi lại được xác định để cung cấp cho bạn số dòng trên đó ">" của thẻ bắt đầu xuất hiện.)

+0

Xin chào, tôi có thể cấu hình bộ vi xử lý XSLT saxon (bất kỳ phiên bản nào) mà nó sử dụng như một trình phân tích cú pháp xml cụ thể không? Tôi chỉ tìm thấy tham số -x để sử dụng trình phân tích SAX của riêng mình. –

+0

Saxon có tùy chọn cấu hình -l hoặc FeatureKeys.LINE_NUMBERING sẽ làm cho nó thu thập thông tin số dòng được cung cấp bởi trình phân tích cú pháp XML và giữ lại nó trong cây được xây dựng. Sau đó nó có thể truy cập được bằng hàm mở rộng saxon: line-number(). –

+0

cảm ơn câu trả lời. tôi biết hàm saxon: line-number. Tôi xin lỗi tôi đã không đủ chính xác! Câu trả lời của priomsrb cho phép tôi modifie PositionalXMLReader của mình để thêm nhiều dữ liệu người dùng hơn cho các nút. Tôi đã tìm thấy hàm saxon: getUserData (chỉ dành cho các phiên bản <7.4?) và đã tự hỏi liệu tôi có thể sử dụng điều đó để có thêm thông tin về các nút trực tiếp vào XSLT hay không. (e. g số hàng/cột cuối cùng của nút.) –

-2

Lưu ý rằng theo spec (của Locator.getLineNumber()) phương thức trả về số dòng nơi SAX-event kết thúc!

Trong trường hợp của "startElement()" điều này có nghĩa:

đây số dòng cho tử là :

<Element></Element> 

đây số dòng cho tử là :

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

Xin chào @hhaehle. Chào mừng bạn đến với SO. Đây là một số thông tin hữu ích nhưng nó có lẽ nên được đưa vào một bình luận vì nó không trả lời câu hỏi gốc. Bạn có thể tìm hiểu thêm về nhận xét [tại đây] (https://stackoverflow.com/help/privileges/comment). – Chic

Các vấn đề liên quan