Конечно, я мог бы использовать регулярные выражения для анализа данных из 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), которую я могу использовать для указания данных, которые я хочу извлечь.
Другими словами, я ищу решение, которое более или менее может быть сконфигурировано вместо запрограммированного, потому что у меня есть довольно много подобных определений для извлечения.
Это короткое выражение 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
Вы можете использовать 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>
.
<message>
есть ровно одна <part>
.
concat(//message/@name,' | ', //message/part/@name,' | ', substring-after( //message/part/@element, 'xsd:'))
возвращает прекрасный результат notificationInput | body | notificationRequest
. Но мне интересно, почему только первый матч?
Я знаю, что разбор 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'
array(...)
- это ожидаемый результат, верно?