Как получить правильные начальные / конечные местоположения тега xml с SAX?
В SAX есть локатор, который отслеживает текущее местоположение. Однако, когда я вызываю его в своем startElement(), он всегда возвращает мне конечное местоположение тега xml.
Как я могу получить начальное местоположение тега? Есть ли способ изящно решить эту проблему?
3 ответа
К сожалению, Locator
интерфейс, предоставляемый системной библиотекой Java в org.xml.sax
Пакет не позволяет получить более подробную информацию о местонахождении документации по определению. Цитировать из документации getColumnNumber
метод (основные моменты добавлены мной):
Возвращаемое значение из метода предназначено только в качестве приблизительного значения для диагностики; он не предназначен для предоставления достаточной информации для редактирования символьного содержимого исходного документа XML. Например, если строки содержат сочетания последовательностей символов, широкие символы, суррогатные пары или двунаправленный текст, значение может не соответствовать столбцу в текстовом редакторе.
В соответствии с этой спецификацией вы всегда получите позицию " первого символа после текста, связанного с событием документа ", основываясь на максимальных усилиях драйвера SAX. Итак, краткий ответ на первую часть вашего вопроса: нет, Locator
не предоставляет информацию о начальном местоположении тега. Кроме того, если вы имеете дело с многобайтовыми символами в ваших документах, например, китайским или японским текстом, позиция, которую вы получаете от драйвера SAX, вероятно, не та, которую вы хотите.
Если вам нужны точные позиции для тегов или вы хотите получить более подробную информацию об атрибутах, содержимом атрибутов и т. Д., Вам придется реализовать свой собственный поставщик местоположений.
Принимая во внимание все потенциальные проблемы кодирования, символы Unicode и т. Д., Я думаю, что это слишком большой проект для размещения здесь, реализация также будет зависеть от ваших конкретных требований.
Просто быстрое предупреждение из личного опыта: написание обёртки вокруг InputStream
переход в SAX-парсер опасен, так как вы не знаете, когда SAX-парсер сообщит о своих событиях на основе того, что он уже прочитал из потока.
Вы могли бы начать с собственного подсчета в characters(char[], int, int)
метод вашего ContentHandler
проверяя разрывы строк, вкладки и т. д. в дополнение к использованию Locator
информация, которая должна дать вам лучшее представление о том, где в документе вы на самом деле находитесь. Запомнив позиции последнего события, вы можете рассчитать начальную позицию текущего события. Примите во внимание, что вы можете увидеть не все разрывы строк, так как они могут появляться внутри тегов, которые вы не увидите в characters
, но вы могли бы вывести те из Locator
Информация.
Здесь приходит решение, которое я наконец-то понял. (Но мне было лень это поднимать, извините.) Здесь методы characters(), endElement() и ignorableWhitespace() имеют решающее значение, с локатором они указывают на возможную отправную точку ваших тегов. Локатор в characters () указывает на ближайшую конечную точку информации, не являющейся тегом, локатор в endElement () указывает на конечную позицию последнего тега, который, возможно, будет начальной точкой этого тега, если они слипаются, и локатор в ignorableWhitespace() указывает на конец ряда пробелов и табуляции. Пока мы отслеживаем конечную позицию этих трех методов, мы можем найти начальную точку для этого тега и уже можем получить конечную позицию этого тега с помощью локатора в endElement(). Следовательно, начальная точка и конечная точка xml могут быть успешно найдены.
class Example extends DefaultHandler{
private Locator locator;
private SourcePosition startElePoint = new SourcePosition();
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
/**
* <a> <- the locator points to here
* <b>
* </a>
*/
public void startElement(String uri, String localName,
String qName, Attributes attributes) {
}
/**
* <a>
* <b>
* </a> <- the locator points to here
*/
public void endElement(String uri, String localName, String qName) {
/* here we can get our source position */
SourcePosition tag_source_starting_position = this.startElePoint;
SourcePosition tag_source_ending_position =
new SourcePosition(this.location.getLineNumber(),
this.location.getColumnNumber());
// do your things here
//update the starting point for the next tag
this.updateElePoint(this.locator);
}
/**
* some other words <- the locator points to here
* <a>
* <b>
* </a>
*/
public void characters(char[] ch, int start, int length) {
this.updateElePoint(this.locator);//update the starting point
}
/**
*the locator points to here-> <a>
* <b>
* </a>
*/
public void ignorableWhitespace(char[] ch, int start, int length) {
this.updateElePoint(this.locator);//update the starting point
}
private void updateElePoint(Locator lo){
SourcePosition item = new SourcePosition(lo.getLineNumber(), lo.getColumnNumber());
if(this.startElePoint.compareTo(item)<0){
this.startElePoint = item;
}
}
class SourcePosition<SourcePosition> implements Comparable<SourcePosition>{
private int line;
private int column;
public SourcePosition(){
this.line = 1;
this.column = 1;
}
public SourcePosition(int line, int col){
this.line = line;
this.column = col;
}
public int getLine(){
return this.line;
}
public int getColumn(){
return this.column;
}
public void setLine(int line){
this.line = line;
}
public void setColumn(int col){
this.column = col;
}
public int compareTo(SourcePosition o) {
if(o.getLine() > this.getLine() ||
(o.getLine() == this.getLine()
&& o.getColumn() > this.getColumn()) ){
return -1;
}else if(o.getLine() == this.getLine() &&
o.getColumn() == this.getColumn()){
return 0;
}else{
return 1;
}
}
}
}
Какой SAX-парсер вы используете? Некоторые, как мне сказали, не предоставляют услуги Locator.
Вывод простой программы на Python ниже даст вам начальную строку и номер столбца каждого элемента в вашем XML-файле, например, если вы сделаете отступ в два пробела в вашем XML:
Element: MyRootElem
starts at row 2 and column 0
Element: my_first_elem
starts at row 3 and column 2
Element: my_second_elem
starts at row 4 and column 4
Запустите так: python sax_parser_filename.py my_xml_file.xml
#!/usr/bin/python
import sys
from xml.sax import ContentHandler, make_parser
from xml.sax.xmlreader import Locator
class MySaxDocumentHandler(ContentHandler):
"""
the document handler class will serve
to instantiate an event handler which will
acts on various events coming from the parser
"""
def __init__(self):
self.setDocumentLocator(Locator())
def startElement(self, name, attrs):
print "Element: %s" % name
print "starts at row %s" % self._locator.getLineNumber(), \
"and column %s\n" % self._locator.getColumnNumber()
def endElement(self, name):
pass
def mysaxparser(inFileName):
# create a handler
handler = MySaxDocumentHandler()
# create a parser
parser = make_parser()
# associate our content handler to the parser
parser.setContentHandler(handler)
inFile = open(inFileName, 'r')
# start parser
parser.parse(inFile)
inFile.close()
def main():
mysaxparser(sys.argv[1])
if __name__ == '__main__':
main()