priomsrb’s answer is great and works. For my usecase i need to integrate it to an existing framework where e.g. the encoding is also covered. Therefore the following refactoring was applied to have a separate LineNumberHandler class.
Then the code will also work with a Sax InputSource where the encoding can be modified like this:
// read in the xml document
org.xml.sax.InputSource is=new org.xml.sax.InputSource();
is.setByteStream(instream);
if (encoding!=null) {
is.setEncoding(encoding);
if (Debug.CORE)
Debug.log("setting XML encoding to - "+is.getEncoding());
}
Separate LineNumberHandler
/**
* LineNumber Handler
* @author wf
*
*/
public static class LineNumberHandler extends DefaultHandler {
final Stack<Element> elementStack = new Stack<Element>();
final StringBuilder textBuffer = new StringBuilder();
private Locator locator;
private Document doc;
/**
* create a line number Handler for the given document
* @param doc
*/
public LineNumberHandler(Document doc) {
this.doc=doc;
}
@Override
public void setDocumentLocator(final 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(final String uri, final String localName,
final String qName, final Attributes attributes) throws SAXException {
addTextIfNeeded();
final Element el = doc.createElement(qName);
for (int i = 0; i < attributes.getLength(); i++) {
el.setAttribute(attributes.getQName(i), attributes.getValue(i));
}
el.setUserData(LINE_NUMBER_KEY_NAME,
String.valueOf(this.locator.getLineNumber()), null);
elementStack.push(el);
}
@Override
public void endElement(final String uri, final String localName,
final String qName) {
addTextIfNeeded();
final Element closedEl = elementStack.pop();
if (elementStack.isEmpty()) { // Is this the root element?
doc.appendChild(closedEl);
} else {
final Element parentEl = elementStack.peek();
parentEl.appendChild(closedEl);
}
}
@Override
public void characters(final char ch[], final int start, final int length)
throws SAXException {
textBuffer.append(ch, start, length);
}
// Outputs text accumulated under the current node
private void addTextIfNeeded() {
if (textBuffer.length() > 0) {
final Element el = elementStack.peek();
final Node textNode = doc.createTextNode(textBuffer.toString());
el.appendChild(textNode);
textBuffer.delete(0, textBuffer.length());
}
}
};
PositionalXMLReader
public class PositionalXMLReader {
final static String LINE_NUMBER_KEY_NAME = "lineNumber";
/**
* read a document from the given input strem
*
* @param is
* - the input stream
* @return - the Document
* @throws IOException
* @throws SAXException
*/
public static Document readXML(final InputStream is)
throws IOException, SAXException {
final Document doc;
SAXParser parser;
try {
final SAXParserFactory factory = SAXParserFactory.newInstance();
parser = factory.newSAXParser();
final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
.newInstance();
final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
doc = docBuilder.newDocument();
} catch (final ParserConfigurationException e) {
throw new RuntimeException("Can't create SAX parser / DOM builder.", e);
}
LineNumberHandler handler = new LineNumberHandler(doc);
parser.parse(is, handler);
return doc;
}
}
JUnit Testcase
package com.bitplan.common.impl;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.bitplan.bobase.PositionalXMLReader;
public class TestXMLWithLineNumbers {
/**
* get an Example XML Stream
* @return the example stream
*/
public InputStream getExampleXMLStream() {
String xmlString = "<foo>n" + " <bar>n"
+ " <moo>Hello World!</moo>n" + " </bar>n" + "</foo>";
InputStream is = new ByteArrayInputStream(xmlString.getBytes());
return is;
}
@Test
public void testXMLWithLineNumbers() throws Exception {
InputStream is = this.getExampleXMLStream();
Document doc = PositionalXMLReader.readXML(is);
is.close();
Node node = doc.getElementsByTagName("moo").item(0);
assertEquals("3", node.getUserData("lineNumber"));
}
}
Здравствуйте. Ситуация: есть модель(дисериализованный файл), довольно большая. В ней много строк и вложенных объектов. (Модель YML каталога).
[XmlRoot("yml_catalog")]
public class YMLCatalog
{
/// <summary>
/// Дата и время генерации YML файла на стороне магазина
/// </summary>
[Required]
[XmlAttribute("date")]
public string Date { get; set; }
/// <summary>
/// Магазин
/// </summary>
[Required]
[ValidateObject]
[XmlElement(ElementName = "shop")]
public Shop Shop { get; set; }
}
/// <summary>
/// Точка продаж
/// https://yandex.ru/support/webmaster/goods-prices/technical-requirements.html
/// </summary>
public class Shop
{
/// <summary>
/// Название магазина.
/// </summary>
[Required]
[XmlElement("name")]
public string Name { get; set; }
/// <summary>
/// Название компании
/// </summary>
[Required]
[XmlElement("company")]
public string Company { get; set; }
... и тд.
}
При валидации модели, в случае ошибки, я получаю (при помощи магии. См. краткое описание в комментариях) путь к ошибке. Например для YML файла с ошибкой в теге price, 0го оффера, в виде отсутствия цены, я получаю путь: shop/offers/offer[‘0 или 1’, смотря как удобнее. Для XPath лучше 1]/price Value: Текст ошибки. (Value – поле со значением тега, аналогично может быть для атрибутов)
Нас интересует: shop/offers/offer[0]/price
По этому пути, мне нужно определить номер строки, где находиться этот тег. Чтобы указать пользователю где ошибка.
Что я пробовал:
1) Сделал через регулярные выражения. Сначала последовательно нахожу индекс элементов по пути, как получу индекс начала тега Price в файле. Затем собираю список строк в файле и нахожу номер строки для вывода, относительно всего файла. – Это работает, но меня не устраивает. Слишком медленно, а у меня могут быть очень большие файлы
2) Попробовал сделать через XPath. Т.е например можно легко получить элемент тега вот так:
XmlDocument document = new XmlDocument();
document.LoadXml(text);
var node = document.SelectSingleNode("//shop/offers/offer[1]/price");// это для примера
3) Есть вариант с созданием своего дессериализатора + атрибутов валидации для него, чтобы к каждому полю в модели приписывать в метаданных IXmlLineInfo – который в стандартном XmlSerializer к сожалению используется только при выдаче информации об ошибке – но это долго, а те готовые, что я нашел, не поддерживаются в net core… UPD: также в таком случае требуется поддержка не только составных объектов, но и простых + простых атрибутов (string, int, long и тп)
Вопрос 1: XPath как-нибудь позволяет получить кол-во элементов выше (или лучше их список) ? Вроде комбинировать preceding и ancestor нельзя (ссылка) – Но опять же, даже если это получиться, придется искать индекс, создавая список строк файла. – Не очень хорошо, хотелось бы узнать номер строки ошибки другим способом
Пробовал “//shop/offers/offer[ 1 ]/price/preceding::*”, но в таком случае игнорируются все элементы выше shop, почему не знаю. (когда использовал count(…), получал 27 элементов) – Думаю это всяко лучше только регулярных выражений
Знаете еще способ? Прошу в ответы:)
Главный вопрос: Так как лучше всего найти номер строки ошибки, имея XML и путь к тегу?
P.S Производительность решения приветствуется)
У меня это работает, следуя этому примеру:
Это решение следует методу, предложенному Майклом Кей. Вот как вы его используете:
// XmlTest.java
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class XmlTest {
public static void main(final String[] args) throws Exception {
String xmlString = "<foo>n"
+ " <bar>n"
+ " <moo>Hello World!</moo>n"
+ " </bar>n"
+ "</foo>";
InputStream is = new ByteArrayInputStream(xmlString.getBytes());
Document doc = PositionalXMLReader.readXML(is);
is.close();
Node node = doc.getElementsByTagName("moo").item(0);
System.out.println("Line number: " + node.getUserData("lineNumber"));
}
}
Если вы запустите эту программу, она выйдет: “Номер строки: 3”
PositionalXMLReader – это слегка измененная версия приведенного выше примера.
// PositionalXMLReader.java
import java.io.IOException;
import java.io.InputStream;
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.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 PositionalXMLReader {
final static String LINE_NUMBER_KEY_NAME = "lineNumber";
public static Document readXML(final InputStream is) throws IOException, SAXException {
final Document doc;
SAXParser parser;
try {
final SAXParserFactory factory = SAXParserFactory.newInstance();
parser = factory.newSAXParser();
final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
doc = docBuilder.newDocument();
} catch (final ParserConfigurationException e) {
throw new RuntimeException("Can't create SAX parser / DOM builder.", e);
}
final Stack<Element> elementStack = new Stack<Element>();
final StringBuilder textBuffer = new StringBuilder();
final DefaultHandler handler = new DefaultHandler() {
private Locator locator;
@Override
public void setDocumentLocator(final 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(final String uri, final String localName, final String qName, final Attributes attributes)
throws SAXException {
addTextIfNeeded();
final Element el = doc.createElement(qName);
for (int i = 0; i < attributes.getLength(); i++) {
el.setAttribute(attributes.getQName(i), attributes.getValue(i));
}
el.setUserData(LINE_NUMBER_KEY_NAME, String.valueOf(this.locator.getLineNumber()), null);
elementStack.push(el);
}
@Override
public void endElement(final String uri, final String localName, final String qName) {
addTextIfNeeded();
final Element closedEl = elementStack.pop();
if (elementStack.isEmpty()) { // Is this the root element?
doc.appendChild(closedEl);
} else {
final Element parentEl = elementStack.peek();
parentEl.appendChild(closedEl);
}
}
@Override
public void characters(final char ch[], final int start, final int length) throws SAXException {
textBuffer.append(ch, start, length);
}
// Outputs text accumulated under the current node
private void addTextIfNeeded() {
if (textBuffer.length() > 0) {
final Element el = elementStack.peek();
final Node textNode = doc.createTextNode(textBuffer.toString());
el.appendChild(textNode);
textBuffer.delete(0, textBuffer.length());
}
}
};
parser.parse(is, handler);
return doc;
}
}
От: |
ra88 |
||
Дата: | 24.09.10 06:56 | ||
Оценка: |
Доброго рабочего дня!
собственно имеется XML фаил, который считывается по средством XmlDocument.Load(filename)
Затем я бегаю по дереву с помощю SelectNodes() и ChildNodes. В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
Как это сделать?
while true;
Re: номер строки в XML файле
От: |
Arnx
|
||
Дата: | 24.09.10 07:00 | ||
Оценка: |
R>собственно имеется XML фаил, который считывается по средством XmlDocument.Load(filename)
R>Затем я бегаю по дереву с помощю SelectNodes() и ChildNodes. В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
R>Как это сделать?
Вообще-то говоря, строк как таковых в хмл документе нету. Если хочется знать “как бы представление” то можешь держать текущую глубину элемента = собственно все пройденные узлы *(или как будешь справляться с закрывающими тегами) + дети.
Может тебе проще параллельно загнать построчно в хэш таблицу и вытаскивать оттуда по какому-либо ключу?
Re: номер строки в XML файле
От: |
HowardLovekraft |
||
Дата: | 24.09.10 07:03 | ||
Оценка: |
Здравствуйте, ra88, Вы писали:
R>В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
А если XML хранится в файле в одну строку:
<root><child>123</child></root>
?
Re[2]: номер строки в XML файле
От: |
ra88 |
||
Дата: | 24.09.10 07:07 | ||
Оценка: |
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, ra88, Вы писали:
R>>В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
HL>А если XML хранится в файле в одну строку:
HL>
HL><root><child>123</child></root>
HL>?
тогда номер строки 1.
while true;
Re: номер строки в XML файле
От: |
Closer |
||
Дата: | 24.09.10 07:09 | ||
Оценка: |
20 (4) |
Здравствуйте, ra88, Вы писали:
[skipped]
R>Как это сделать?
Для начала научится пользоваться поиском.
Здесь
Автор: Closer
Дата: 17.10.06
решение.
Мы были здесь. Но пора идти дальше. (с) Дуглас Коупленд, Рабы “Микрософт”
Re[2]: номер строки в XML файле
От: |
ra88 |
||
Дата: | 24.09.10 07:15 | ||
Оценка: |
Здравствуйте, Closer, Вы писали:
C>Здравствуйте, ra88, Вы писали:
C>[skipped]
R>>Как это сделать?
C>Для начала научится пользоваться поиском. Здесь
Автор: Closer
Дата: 17.10.06
решение.
while true;
Re: номер строки в XML файле
От: |
_FRED_
|
@ViIvanov | |
Дата: | 24.09.10 10:30 | ||
Оценка: |
8 (3) |
Здравствуйте, ra88, Вы писали:
R>собственно имеется XML фаил, который считывается по средством XmlDocument.Load(filename)
R>Затем я бегаю по дереву с помощю SelectNodes() и ChildNodes. В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
R>Как это сделать?
А использовать современное API (System.Xml.Linq) для работы с xml возможность есть? В нём уже каждый объект реализует System.Xml.IXmlLineInfo.
Help will always be given at Hogwarts to those who ask for it.
Re[2]: номер строки в XML файле
От: |
ra88 |
||
Дата: | 24.09.10 11:35 | ||
Оценка: |
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, ra88, Вы писали:
R>>собственно имеется XML фаил, который считывается по средством XmlDocument.Load(filename)
R>>Затем я бегаю по дереву с помощю SelectNodes() и ChildNodes. В определённый момент хочется узнать на какой строке в файле находится открывающий тэг выбранного XmlNode.
R>>Как это сделать?
_FR>А использовать современное API (System.Xml.Linq) для работы с xml возможность есть? В нём уже каждый объект реализует System.Xml.IXmlLineInfo.
пока нет. надо исходить из того, что имеется только .NET 2.0
Но спасибо за информацию. В будущем пригодится.
while true;
- Переместить
- Удалить
- Выделить ветку
Пока на собственное сообщение не было ответов, его можно удалить.
Скажем, у меня есть этот XML-файл: https: // prod -c2g.s3.amazonaws.com/db/Winter2013/files/courses-noID.xml
Есть ли способ показать номера строк на веб-странице или вам нужно скопировать их на Eclipse, чтобы показать номера строк?
Я хочу избежать копирования в Eclipse, потому что тогда я не могу разворачивать или сворачивать теги.
1
committedandroider
30 Авг 2014 в 04:11
1
Ваш браузер полностью контролирует внешний вид документов или веб-страниц. Некоторые браузеры, безусловно, будут отображать XML-документы без таблиц стилей с индивидуально пронумерованными строками.
–
Sam Varshavchik
30 Авг 2014 в 04:12
Спасибо, знаете ли вы о браузере, который показывает номера строк с XML-документами?
–
committedandroider
30 Авг 2014 в 04:14
1
Если вы просматриваете исходный код в хроме, он покажет номера строк (при условии, что ваш исходный XML-файл разделен на разные строки).
–
terrywb
30 Авг 2014 в 04:15
«Просмотр исходного кода страницы» в Mozilla Firefox также отображает XML-документ с пронумерованными строками.
–
Sam Varshavchik
30 Авг 2014 в 04:18
1 ответ
Лучший ответ
Одна альтернатива – копирование в другой текстовый редактор, например Notepad ++, в котором есть возможность сворачивания бирки.
Это не идеально, но это решение, которое дает вам номера строк и сворачивание кода.
3
Peter Mortensen
12 Авг 2020 в 21:31
Похожие вопросы
Новые вопросы
xml
XML (Extensible Markup Language) – это формат структурированного документа, определяющий правила кодирования текста. При использовании этого тега включайте дополнительные теги, такие как язык программирования, наборы инструментов, используемые технологии XML и другие теги, описывающие среду, в которой опубликована проблема. Гибкость XML обеспечивает широкий спектр применений для передачи человеческих и машинных данных, в том числе конкретных инструментов и библиотек.
Подробнее про xml…