Search code examples
javaxmldomsaxdom4j

Use dom4j to locate the node with line number


I used dom4j to parse a xml file and I got a document with line numbers, I want to use the line numbers to locate all nodes with line numbers and to operate these nodes.

Is there a way to use dom4j or other DOM API to implement this?


Solution

  • It is possible to extend DOM4J to include line numbers for element, but it is a bit convoulted, and not 100% exact (you can get the line number of the ">" character of the opening element).

    Probably, a much more robust approach would be to report an XPath expression for each of the offending elements, and then use these expressions to fix it.

    However, just for fun, here is a complete example of how to include line number information in DOM4J:

    public class LineNumber {
    
        public static void main(String[] args) throws Exception {
    
            SAXReader reader = new MySAXReader();
            reader.setDocumentFactory(new LocatorAwareDocumentFactory());
    
            Document doc = reader
                    .read(new StringReader("<root foo='bar' > \n<alfa/>\n<beta/>\n<gamma/>\n</root>\n"));
            doc.accept(new VisitorSupport() {
                @Override
                public void visit(Element node) {
                    System.out.printf("%d: %s\n",
                            ((LocationAwareElement) node).getLineNumber(),
                            node.getName());
                }
            });
    
        }
    
        static class MySAXReader extends SAXReader {
    
            @Override
            protected SAXContentHandler createContentHandler(XMLReader reader) {
                return new MySAXContentHandler(getDocumentFactory(),
                        getDispatchHandler());
            }
    
            @Override
            public void setDocumentFactory(DocumentFactory documentFactory) {
                super.setDocumentFactory(documentFactory);
            }
    
        }
    
        static class MySAXContentHandler extends SAXContentHandler {
    
            private Locator locator;
    
            // this is already in SAXContentHandler, but private
            private DocumentFactory documentFactory;
    
            public MySAXContentHandler(DocumentFactory documentFactory,
                    ElementHandler elementHandler) {
                super(documentFactory, elementHandler);
                this.documentFactory = documentFactory;
            }
    
            @Override
            public void setDocumentLocator(Locator documentLocator) {
                super.setDocumentLocator(documentLocator);
                this.locator = documentLocator;
                if (documentFactory instanceof LocatorAwareDocumentFactory) {
                    ((LocatorAwareDocumentFactory)documentFactory).setLocator(documentLocator);
                }
    
            }
    
            public Locator getLocator() {
                return locator;
            }
        }
    
        static class LocatorAwareDocumentFactory extends DocumentFactory {
    
            private Locator locator;
    
            public LocatorAwareDocumentFactory() {
                super();
            }
    
            public void setLocator(Locator locator) {
                this.locator = locator;
            }
    
            @Override
            public Element createElement(QName qname) {
                LocationAwareElement element = new LocationAwareElement(qname);
                if (locator != null)
                    element.setLineNumber(locator.getLineNumber());
                return element;
            }
    
        }
    
        /**
         * An Element that is aware of it location (line number in) in the source document
         */
        static class LocationAwareElement extends DefaultElement {
    
            private int lineNumber = -1;
    
            public LocationAwareElement(QName qname) {
                super(qname);
            }
    
            public LocationAwareElement(QName qname, int attributeCount) {
                super(qname, attributeCount);
    
            }
    
            public LocationAwareElement(String name, Namespace namespace) {
                super(name, namespace);
    
            }
    
            public LocationAwareElement(String name) {
                super(name);
    
            }
    
            public int getLineNumber() {
                return lineNumber;
            }
    
            public void setLineNumber(int lineNumber) {
                this.lineNumber = lineNumber;
            }
    
        }
    
    }