Почему я не получаю пробелов между значениями текстовых узлов?

Я использую выражение Xpath для получения текстовых узлов из XML-документа, как показано ниже:

<company>
    <emp>
        <dept>Acct</dept>
        <salary>1000</salary>
        <proj>
            <under>E01</under>
             <under>E02</under>
        </proj>
        <name>John Doe</name>
        <gender>male</gender>
    </emp>
</company>

Я написал следующее выражение XPATH для получения текстовых значений:

normalize-space(string(//emp))

Он извлекает правильные значения, и результат выглядит следующим образом:

Acct1000E01E02John Doemale

Обратите внимание, что между текстовыми значениями узлов из разных узлов нет пробелов.

Я на самом деле хочу, чтобы выходное значение было таким:

`Acct 1000 E01 E02 John Doe`

Я использовал javax.xml.xpath для анализа и построения дерева следующим образом:

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 Document document = builder.parse(new File("/employees.xml"));

 XPath xpath = XPathFactory.newInstance().newXPath();
 String expression = "normalize-space(string(//emp))";
 String output= (String)xPath.compile(expression).evaluate(document, XPathConstants.STRING);

Я использую JAVA SE 10 здесь. Итак, версия Xpath 1.0

Есть ли лучший способ извлечь текстовые значения? Я новичок в XPath, поэтому любые предложения будут полезны.


person A Beginner    schedule 30.11.2018    source источник


Ответы (2)


Вы почти правы здесь. Правильный выбор оператора not. . Это должно быть что-то вроде этого:

/html/body/company/emp/*[not(self::gender)]

То есть все дочерние узлы emp, кроме узла gender.
Вот полный пример в javascript:

let xpathExpression = '/html/body/company/emp/*[not(self::gender)]';
let contextNode = window.document;
let xpathResult = document.evaluate(xpathExpression, contextNode, 
                                        null, XPathResult.ANY_TYPE, null);

console.log(xpathResult.iterateNext());
console.log(xpathResult.iterateNext());
console.log(xpathResult.iterateNext());
console.log(xpathResult.iterateNext());
person felipsmartins    schedule 30.11.2018
comment
Спасибо. Но это только выполняет мое второе требование. Можете ли вы предложить мне, как выполнить мое первое требование? - person A Beginner; 30.11.2018

О боже, это сложно...

Прежде всего, вы не пометили свой вопрос версией XPath. Обычно люди, которые не знают о версиях XPath, используют древнюю версию 1.0, поэтому я сделаю такое предположение: извините, если что-то не так.

В XPath 1.0 функция, получившая набор узлов и ожидающая строку, использует строковое значение первого узла в наборе узлов, взятое в порядке документа.

В вашем запросе

normalize-space(string(//emp))

//emp выбирает набор узлов, который содержит один узел, поэтому string() принимает строковое значение этого узла. Строковое значение узла элемента представляет собой конкатенацию всех его дочерних текстовых узлов. Функция normalize-space удаляет начальные и конечные пробелы и нормализует внутреннее пространство до одного символа пробела.

Вы показали свой XML в форме с отступом как

<company>
    <emp>
        <dept>Acct</dept>
        <salary>1000</salary>

и т. д., поэтому разумно ожидать, что пробелы между элементами образуют часть строкового значения элемента <emp>. Но вы не рассказали нам, как документ был проанализирован и преобразован в дерево узлов. Парсеры часто предоставляют несколько вариантов того, как это сделать, в частности, как обрабатывать пробелы между узлами элементов. Большинство сохраняют пробелы по умолчанию, если, возможно, нет схемы или DTD, которые сообщают синтаксическому анализатору, что пробелы несущественны. Известно, что синтаксический анализатор Microsoft MSXML по умолчанию отбрасывает пробелы, что вызывает значительные проблемы, когда вы используете XML для представления описательных документов, но на самом деле облегчает жизнь людям, использующим XML для такого рода недокументированных данных.

Ваш синтаксический анализатор по той или иной причине (мы не можем сказать), похоже, удалил пробелы между узлами элементов. Никакой запрос XPath не вернет его снова. У вас могут быть варианты при создании документа, чтобы сохранить пробелы; это зависит от инструментов, которые вы используете.

Ваш второй вопрос касается удаления одного из элементов ввода. Это выходит за рамки XPath. XPath может только выбирать узлы из ввода, он не может их каким-либо образом модифицировать. Чтобы изменить дерево, вам понадобится XSLT или XQuery.

Ваша попытка решить проблему с //emp[not(descendant::gender)] безнадежно обречена, потому что будут выбраны только сотрудники, у которых нет элемента-потомка с именем gender. Похоже, вы угадываете семантику, а не используете спецификацию или учебник.

person Michael Kay    schedule 30.11.2018
comment
Теперь я обновил вопрос с версией Xpath и как я построил дерево. И я удалил второй вопрос, когда задавал его, из-за моего непонимания концепций XPath и Xquery. - person A Beginner; 01.12.2018