Получение всех имен атрибутов элемента XML с помощью xpath на xmlstarlet

Я вижу, как получить все значения атрибутов:
xml sel -t -v "//element/@*"

но я хочу получить имена всех атрибутов.
Я могу получить n-е имя, например, xml sel -t -v "name(//x:mem/@*[3])", которое возвращает имя третьего атрибута.
но xml sel -t -v "name(//x:mem/@*)" не работает (возвращает только имя 1-го атрибута)...

Есть ли способ получить имена атрибутов все?


person Amir Gonnen    schedule 26.01.2015    source источник
comment
Я не думаю, что XPath может вернуть список строк, так что нет. Однако вы можете использовать XSLT для создания XML или текстового вывода с именами атрибутов.   -  person ThW    schedule 26.01.2015
comment
@ThW Если я могу вернуть список значений атрибутов, почему бы не найти способ вернуть список имен атрибутов?   -  person Amir Gonnen    schedule 26.01.2015
comment
Атрибуты по-прежнему являются узлами. Таким образом, вы возвращаете список узлов. Инструмент выводит содержимое узла - значения.   -  person ThW    schedule 26.01.2015


Ответы (2)


Используйте -t и -m, чтобы определить соответствие шаблона, а затем примените другое выражение XPath с помощью -v.

$ xml sel -T -t -m "//mem/@*" -v "name()" -n input.xml

применительно к этому входному XML:

<root>
    <mem yes1="1" yes2="2"/>
    <other no="1" no2="2"/>
</root>

будет печатать:

yes1
yes2

Это "короткая строка на оболочке", но она совершенно неразборчива. Поэтому я бы все же предпочел XSLT-решение kjhughes. Не жертвуйте понятным кодом в пользу краткости.

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


Как предложил @npostavs, внутри xmlstarlet все равно используется XSLT. Вы можете проверить сгенерированный XSLT, заменив -T на -C:

$ xml sel -C -t -m "//mem/@*" -v "name()" -n app.xml

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
  <xsl:output omit-xml-declaration="yes" indent="no"/>
  <xsl:template match="/">
    <xsl:for-each select="//mem/@*">
      <xsl:call-template name="value-of-template">
        <xsl:with-param name="select" select="name()"/>
      </xsl:call-template>
      <xsl:value-of select="'&#10;'"/>
    </xsl:for-each>
  </xsl:template>
  <xsl:template name="value-of-template">
    <xsl:param name="select"/>
    <xsl:value-of select="$select"/>
    <xsl:for-each select="exslt:node-set($select)[position()&gt;1]">
      <xsl:value-of select="'&#10;'"/>
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Есть много других вариантов для изучения, см. документацию xmlstarlet.

person Mathias Müller    schedule 26.01.2015
comment
Ах, вы получили это, используя только интерфейс XPath! Очень хорошо. Отдельное использование ключей -t и -m и -v - отличная идея. Поздравляю! - person kjhughes; 26.01.2015
comment
@kjhughes Спасибо - тем не менее, я больше человек типа XSLT ;-). - person Mathias Müller; 26.01.2015
comment
Спасибо @MathiasMüller, мне это решение нравится больше, чем XSLT, хотя я вижу, что ваша точка зрения совершенно непонятна. Однако, пока я пишу его в интерактивном режиме на консоли оболочки только для проверки некоторых XML-файлов (и, предположительно, знаю, что я делаю), я бы предпочел использовать этот один вкладыш. - person Amir Gonnen; 27.01.2015
comment
Замените -T на -C, чтобы преобразовать команду xmlstarlet в (надеюсь, более понятную) XSLT. - person npostavs; 27.01.2015
comment
@npostavs Спасибо за подсказку, приятно знать! Результат удивителен (это не та таблица стилей, которую я бы написал), но ясно, что xmlstarlet все равно использует XSLT. Я отредактирую свой ответ. - person Mathias Müller; 27.01.2015

Эта команда xmlstarlet:

xml tr attnames.xsl in.xml

Используя это преобразование XSLT с именем attnames.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="@*">
    <xsl:value-of select="name(.)"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
  <xsl:template match="*">
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="*"/>
  </xsl:template>
</xsl:stylesheet>

И этот XML-файл с именем in.xml:

<root att1="one">
  <a att2="two"/>
  <b att3="three">
    <c att4="four"/>
  </b>
</root>

Получит список всех атрибутов, найденных в файле in.xml:

att1
att2
att3
att4

Чтобы выбрать все атрибуты только из элемента b, измените XSLT следующим образом:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="@*">
    <xsl:value-of select="name(.)"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
  <xsl:template match="b">
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="*"/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>
person kjhughes    schedule 26.01.2015
comment
Немного громоздко с XSLT... Как я могу выбрать XML-элемент, для которого я хочу перечислить все имена атрибутов? Я не ищу все атрибуты в XML, а только все атрибуты определенного элемента XML (в моем примере //x:mem, но может быть любое другое выражение xpath) - person Amir Gonnen; 26.01.2015
comment
@AmirGonnen В этом нет ничего громоздкого. Если вам нужен более конкретный ответ, покажите свой входной XML. Сказать, что вы хотите //x:mem, недостаточно. В противном случае вы должны принять этот ответ. - person Mathias Müller; 26.01.2015
comment
@MathiasMüller Я жду, не предложит ли кто-нибудь более элегантное решение, иначе я приму этот ответ. И это это громоздко. Вместо короткой строки в оболочке (для получения значений атрибута) мне понадобится сопровождающий скрипт xslt, который мне нужно отредактировать, если я хочу другой элемент. Знаете что, почему бы просто не написать для этого небольшой скрипт на Perl или приложение на C++? Если нам нужно носить с собой сопровождаемый фрагмент кода, давайте пройдем весь путь. - person Amir Gonnen; 26.01.2015
comment
Спасибо, @MathiasMüller. Амир, извини, но я не вижу способа заставить его работать только через интерфейс xmlstarlet XPath 1.0. Если бы xmlstarlet реализовал XPath 2.0 или если бы вы могли использовать средства итерации основного языка в сочетании с XPath 1.0, то я вижу, что XPath 1.0 было бы достаточно. Я не говорю, что это невозможно, но я переступил порог, когда переход к XSLT, который, как мне кажется, xmlstarlet все равно использует за кулисами, стал наиболее целесообразным способом заставить его работать на вас. - person kjhughes; 26.01.2015
comment
@AmirGonnen Есть решение xmlstarlet, но, на мой взгляд, невозможно угадать, что оно делает. - person Mathias Müller; 26.01.2015