XML / Java:解析标签和属性时的精确行和字符位置?

Posted

技术标签:

【中文标题】XML / Java:解析标签和属性时的精确行和字符位置?【英文标题】:XML / Java: Precise line and character positions whilst parsing tags and attributes? 【发布时间】:2017-06-17 12:34:10 【问题描述】:

我试图找到一种方法来在解析 XML 文档时精确地确定标记和属性的行号和字符位置。我想这样做,以便我可以准确地向 XML 文档的作者(通过 Web 界面)报告文档无效的情况。

最终,我想将 a 中的插入符号设置为无效标记或仅在无效属性的打开引号内。 (此时我没有使用 XML Schema,因为属性的确切格式很重要,无法单独通过模式进行验证。我什至可能希望在属性值的中途将某些属性报告为无效。或者类似地,在开始和结束标记之间的文本中途。)

我尝试过使用 SAX (org.xml.sax) 和定位器接口。这在一定程度上有效,但还不够好。它只会在事件之后报告读取位置;例如,对于 startElement(),打开标签结束后的字符。我不能只减去标签名称的长度,因为打开标签中的属性、自闭合标签和/或换行符会将其排除在外。 (而且 Locator 根本不提供有关属性位置的信息。)

理想情况下,我希望使用基于事件的方法,因为我已经有一个 SAX 处理程序,它正在构建内部类似 DOM 的表示或进一步处理。但是,我有兴趣了解任何包含模型元素准确位置信息的 DOM 或类似 DOM 的库。

是否有人以所需的精度解决了这个问题或类似问题?

【问题讨论】:

基于事件的方法?喜欢XMLEventReader 和XMLEvent.getLocation 方法? 我尝试使用的不是 XMLEventReader,而是 XMLStreamReader。但是,此报告的位置是每个事件的结束位置。因此,例如,在 START_ELEMENT 之后,指示的位置紧接在开始标记的关闭之后(注意 - 开始标记,而不是元素)。似乎没有可靠的方法来确定标签开始的位置。此外,我根本没有收到任何 ATTRIBUTE 事件,因为这些事件合并为一个 START_ELEMENT 事件:所以我也无法获得属性位置的任何进一步准确性。 请解释一下当您说您此时没有使用 XML Schema 时的意思,因为属性的确切格式很重要,仅通过模式无法验证。我> @Paul,我和你有同样的挫败感;我想要一个 XML 解析器,它可以为我提供每个元素、属性和文本部分的开始和结束位置,以便我可以编写语法荧光笔,但我在 Java 中找不到任何现成的东西。您是否找到了解决方案,或者您是否编写了自己的词法分析器? @Eric 出于我自己的目的,我已经转而使用自制的 wiki 标记形式,尽管将来我可能需要再次查看基于 XML 的方法。我确实找到了一个 可能 工作的XMLScanner 类(在蜡染中),但我从来没有尝试过。 xmlgraphics.apache.org/batik/javadoc/org/apache/batik/xml/… 【参考方案1】:

XML 解析器将(并且应该)平滑某些内容,例如额外的空格,因此精确映射回字符流是不可行的。

您应该考虑使用词法分析器或“令牌流生成器”来增加细节,换句话说,转到 XML 解析器下面的细节级别。

有一些通用框架可用于在 java 中编写词法分析器。 This 基于 ANTLR 3 的页面很好地概述了词法分析器与解析器和 section one 一些基本的 XML 词法分析器示例。

我还想评论一下,对于使用 Web 界面的用户,也许您应该考虑使用纯客户端(即 javascript)解决方案。

【讨论】:

谢谢。我以前用过 ANTLR,但我不是粉丝。我开始想到我可能必须自己编写一个词法分析器。 交互式 JavaScript 界面是一个长期的好主意。现在,虽然我正在尝试使用嵌入式 XML 岛来创建有效的 wiki 编辑功能,以实现更复杂的标记 - 这些需要在用户保存时解析和验证。 不要自己写,而是像github.com/FasterXML/aalto-xml/blob/master/src/main/java/com/…这样的东西破解【参考方案2】:

我编写了一个快速的 xml 文件,它获取行号并在出现不需要的属性的情况下引发异常,并给出引发错误的文本。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
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.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;



public class LocatorTestSAXReader 
private static final Logger logger =     Logger.getLogger(LocatorTestSAXReader.class);

    private static final String XML_FILE_PATH = "lib/xml/test-instance1.xml";

public Document readXMLFile()

    Document doc = null;
    SAXParser parser = null;

    SAXParserFactory saxFactory = SAXParserFactory.newInstance();
    try 
        parser = saxFactory.newSAXParser();
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
        doc = docBuilder.newDocument();

     catch (ParserConfigurationException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (SAXException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    


    StringBuilder text = new StringBuilder();
    DefaultHandler eleHandler = new DefaultHandler()
        private Locator locator;

        @Override 
        public void characters(char[] ch, int start, int length)
            String thisText = new String(ch, start, length);
            if(thisText.matches(".*[a-zA-z]+.*"))
                text.append(thisText);
                logger.debug("element text: " + thisText);
            

        



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

        @Override
        public void startElement(final String uri, final String localName, final String qName, 
                final Attributes attributes)
                    throws SAXException 
            int lineNum = locator.getLineNumber();
            logger.debug("I am now on line " + lineNum + " at element " + qName);

            int len = attributes.getLength();
            for(int i=0;i<len;i++)
                String attVal = attributes.getValue(i);
                String attName = attributes.getQName(i);

                logger.debug("att " + attName + "=" + attVal);

                if(attName.startsWith("bad"))
                    throw new SAXException("found attr : " + attName + "=" + attVal + " that starts with bad! at line : " + 
                locator.getLineNumber() + " at element " + qName +   "\nelement occurs below text : " + text);
                
            

        




    ;

    try 
        parser.parse(new FileInputStream(new File(XML_FILE_PATH)), eleHandler);
     catch (FileNotFoundException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (SAXException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (IOException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        

        return doc;
    



关于文本,取决于 xml 文件中发生错误的位置,可能没有任何文本。所以有了这个xml:

<?xml version="1.0"?>
<root>
  <section>
    <para>This is a quick doc to test the ability to get line numbers via the Locator object. </para>
  </section>    
  <section bad:attr="ok">
    <para>another para.</para>
  </section>
</root>

如果错误属性在第一个元素中,则文本将为空白。在这种情况下,抛出的异常是:

org.xml.sax.SAXException: found attr : bad:attr=ok that starts with bad! at line : 6 at element section
element occurs below text : This is a quick doc to test the ability to get line numbers via the Locator object. 

当您说您尝试使用 Locator 对象时,究竟是什么问题?

【讨论】:

我想知道(使用您的示例)“bad:attr”的“b”的确切行和列位置。或者 - 如果属性的值是问题 - 开放引号或“ok”的“o”。 但在其他情况下,它可能是“
”的确切位置,例如,如果
不是 内的有效元素。或“另一个段落”的“a”。如果,说,“另一个段落。”不是在 之间找到的有效字符串。一般来说,我想知道开始/结束标签的确切行和列位置、文本运行、属性名称和属性值。

以上是关于XML / Java:解析标签和属性时的精确行和字符位置?的主要内容,如果未能解决你的问题,请参考以下文章

Java基础-xml解析

java解析xml获取标签属性值,面试题+笔记+项目实战

java xml解析方式(DOMSAXJDOMDOM4J)

java解析xml获取标签属性值,跳槽大厂必看!

如何在解析之前检查 XML 中是不是存在属性和标签?

Java 解析 XML