Java 有没有一种方法可以通过SAX/DOM解析XML,每个节点都有行号

Java 有没有一种方法可以通过SAX/DOM解析XML,每个节点都有行号,java,xml,dom,sax,Java,Xml,Dom,Sax,我已经为一种大型XML文档格式编写了DOM解析器,该格式包含许多可用于自动生成Java代码的项。这仅限于将小表达式合并到动态生成的Java源文件中 到目前为止,一切都很好。一切正常 但是-我希望能够嵌入包含Java代码的XML节点的行号(这样,如果配置包含不可编译的代码,每个方法都将有一个指向源XML文档的指针和行号,以便于调试)。我在解析时不需要行号,也不需要验证XML源文档并在特定行号处抛出错误。我需要能够访问DOM或每个SAX事件中每个节点和属性的行号 对我如何实现这一目标有什么建议吗 附

我已经为一种大型XML文档格式编写了DOM解析器,该格式包含许多可用于自动生成Java代码的项。这仅限于将小表达式合并到动态生成的Java源文件中

到目前为止,一切都很好。一切正常

但是-我希望能够嵌入包含Java代码的XML节点的行号(这样,如果配置包含不可编译的代码,每个方法都将有一个指向源XML文档的指针和行号,以便于调试)。我在解析时不需要行号,也不需要验证XML源文档并在特定行号处抛出错误。我需要能够访问DOM或每个SAX事件中每个节点和属性的行号

对我如何实现这一目标有什么建议吗

附言。
此外,我了解到StAX有一种在解析时获取行号的方法,但理想情况下,我希望通过Java 4/5中的常规SAX/DOM处理获得相同的结果,而不是成为Java 6+应用程序或接受额外的.jar文件。

我知道此线程有点旧(抱歉),但是我花了很长时间才解决这个问题,我不得不和别人分享解决方案

您似乎只能通过SAX获得行号,而SAX并不构建DOM。DOM解析器不提供行号,也不允许您接近它正在使用的SAX解析器。我的解决方案是使用SAX源代码和DOM结果进行空XSLT转换,但即便如此,还是有人尽了最大努力隐藏了这一点。请参阅下面的代码

我使用自己的名称空间将位置信息作为属性添加到每个元素中,以便使用XPath查找元素并报告数据的来源

希望这有助于:

// The file to parse.
String systemId = "myxml.xml";

/*
 * Create transformer SAX source that adds current element position to
 * the element as attributes.
 */
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
LocationFilter locationFilter = new LocationFilter(xmlReader);

InputSource inputSource = new InputSource(new FileReader(systemId));
// Do this so that XPath function document() can take relative URI.
inputSource.setSystemId(systemId);
SAXSource saxSource = new SAXSource(locationFilter, inputSource);

/*
 * Perform an empty transformation from SAX source to DOM result.
 */
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMResult domResult = new DOMResult();
transformer.transform(saxSource, domResult);
Node root = domResult.getNode();

...
class LocationFilter extends XMLFilterImpl {

    LocationFilter(XMLReader xmlReader) {
        super(xmlReader);
    }

    private Locator locator = null;

    @Override
    public void setDocumentLocator(Locator locator) {
        super.setDocumentLocator(locator);
        this.locator = locator;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        // Add extra attribute to elements to hold location
        String location = locator.getSystemId() + ':' + locator.getLineNumber() + ':' + locator.getColumnNumber();
        Attributes2Impl attrs = new Attributes2Impl(attributes);
        attrs.addAttribute("http://myNamespace", "location", "myns:location", "CDATA", location);
        super.startElement(uri, localName, qName, attrs);
    }
}

我最近遇到了这个问题,我想我应该共享一个现成的实用程序类来处理它。适用于Java 11,而Reg Whitton的一些代码使用了一些现已弃用的类

主要基于此进行了一些调整。值得注意的是,将行号存储为节点的用户数据,而不是将其设置为属性

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;

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 XmlDom {

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

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

            @Override
            public void setDocumentLocator(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(String uri, String localName, String qName, Attributes attributes) throws SAXException {               
                addTextIfNeeded();
                Element el = doc.createElement(qName);
                for(int i = 0;i < attributes.getLength(); i++)
                    el.setAttribute(attributes.getQName(i), attributes.getValue(i));
                el.setUserData(lineNumAttribName, String.valueOf(locator.getLineNumber()), null);
                elementStack.push(el);               
            }

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

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

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

        return doc;
    }   

}

绝对必要,清晰简洁。我知道这必须存在根据doc,locator.getLineNumber()返回元素的结尾,如果需要开始行号怎么办?可能是?太好了,我会检查它。我问了这个问题,因为我实际上似乎消耗了一些错误信息,声称这在默认的SAX处理器1.4/5.0中是不可能的。如果我成功了,我会在这里报告。谢谢,这正是我要搜索的。根据文档,locator.getLineNumber()返回元素的结尾,如果需要起始行号,该怎么办?
node.getUserData(lineNumAttribName);