Могу ли я использовать XPath или что-то еще, например, регулярное выражение, для извлечения данных из XML?

1

Конечно, я мог бы использовать регулярные выражения для анализа данных из XML.

<?xml version="1.0"?>
<definitions>
  <message name="notificationInput">
    <part name="body" element="xsd:notificationRequest" />
  </message>
  <message name="notificationOutput">
    <part name="body" element="xsd:notificationResponse" />
  </message>
</definitions>

Образец, подобный

/<message.*name="(.*)".*part.*name=".*".*element="xsd:(.*)".*<\/message>/sUg

вероятно, даст мне данные, которые я хочу, здесь показаны как массив PHP:

array(
  array("notificationInput", "body", "notificationRequest"),
  array("notificationOutput", "body", "notificationResponse")
)

Это, конечно, чрезвычайно громоздко и подвержено ошибкам.

Я знаю, как использовать XPath для запроса полных узлов, но я не думаю, что могу сказать "Я хочу, чтобы name атрибута и element из узла /definitions/message/part и для каждого результата я также хочу, чтобы name атрибута из его родителя".

Теперь мне интересно, есть ли какой-либо язык или техника (предпочтительнее реализация на PHP), которую я могу использовать для указания данных, которые я хочу извлечь.

Другими словами, я ищу решение, которое более или менее может быть сконфигурировано вместо запрограммированного, потому что у меня есть довольно много подобных определений для извлечения.

  • 0
    Так что часть array(...) - это ожидаемый результат, верно?
  • 0
    Да, точно, конечно ... (глупая минимальная длина комментария)
Показать ещё 4 комментария
Теги:
xpath

3 ответа

1
Лучший ответ

Это короткое выражение XPath 1.0 выбирает все нужные узлы атрибутов:

/*//*/@*

Затем для каждого выбранного узла вы можете получить его строковое значение с помощью PHP (чего я не знаю).


Если вы можете использовать XPath 2.0, то все нужные значения создаются путем оценки аналогичного выражения:

/*//*/@*/data(.)

Вот простая трансформация XSLT 2.0, которая просто оценивает вышеуказанное выражение и выводит результат:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:sequence select="/*//*/@*/data(.)"/>
  </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному XML-документу:

<definitions>
  <message name="notificationInput">
    <part name="body" element="xsd:notificationRequest" />
  </message>
  <message name="notificationOutput">
    <part name="body" element="xsd:notificationResponse" />
  </message>
</definitions>

Полученный результат получается:

notificationInput body xsd:notificationRequest notificationOutput body xsd:notificationResponse
2

Вы можете использовать XPath

//message/@name|//message[@name]/part/@name|//message/part/@element

для генерации одномерной последовательности всех желаемых атрибутов (извините, это в Python):

In [366]: doc.xpath('//message/@name|//message[@name]/part/@name|//message/part/@element')
Out[366]: 
['notificationInput',
 'body',
 'xsd:notificationRequest',
 'notificationOutput',
 'body',
 'xsd:notificationResponse']

а затем используйте array_chunk чтобы изменить результат в группах по 3. (Обратите внимание, что вам нужно будет использовать немного регулярных выражений или манипуляций с строкой для удаления xsd: из notificationResponse, но это все равно будет намного проще и надежнее, чем при использовании регулярного выражения для анализа XML.

XPath будет собирать все атрибуты, даже если имеется более одного <part> per <message>.

  • 0
    Это работает, только если в каждом <message> есть ровно одна <part> .
  • 0
    concat(//message/@name,' | ', //message/part/@name,' | ', substring-after( //message/part/@element, 'xsd:')) возвращает прекрасный результат notificationInput | body | notificationRequest . Но мне интересно, почему только первый матч?
0

Я знаю, что разбор html с регулярным выражением не рекомендуется, если вы не знаете, что такое набор символов, но я отправляю этот ответ, поскольку он может быть вам полезен.

Для текста примера, который вы предоставили, вы можете использовать простое регулярное выражение:

([a-z]+)"

Рабочая демонстрация

Код Php:

$re = "/([a-z]+)\"/i"; 
$str = "<?xml version=\"1.0\"?>\n<definitions>\n  <message name=\"notificationInput\">\n    <part name=\"body\" element=\"xsd:notificationRequest\" />\n  </message>\n  <message name=\"notificationOutput\">\n    <part name=\"body\" element=\"xsd:notificationResponse\" />\n  </message>\n</definitions>"; 

preg_match_all($re, $str, $matches);

Затем вы можете захватить захваченный контент из $matches.

Информация о матче:

MATCH 1
1.  [53-70] 'notificationInput'
MATCH 2
1.  [89-93] 'body'
MATCH 3
1.  [108-127]   'notificationRequest'
MATCH 4
1.  [162-180]   'notificationOutput'
MATCH 5
1.  [199-203]   'body'
MATCH 6
1.  [218-238]   'notificationResponse'

Ещё вопросы

Сообщество Overcoder
Наверх
Меню