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”。 但在其他情况下,它可能是“以上是关于XML / Java:解析标签和属性时的精确行和字符位置?的主要内容,如果未能解决你的问题,请参考以下文章