Как напечатать расположение совпадения XPath с xmlstarlet? - PullRequest
0 голосов
/ 08 января 2019

Я хотел бы найти ссылки на версии SNAPSHOT в файле pom.xml. Давайте используем файл POM, расположенный здесь для примера. Я нашел следующую команду, чтобы найти элементы, содержащие строку SNAPSHOT:

$ xmlstarlet sel -t -m "//*[contains(text(), 'SNAPSHOT')]" -v . -n pom.xml
0.2-SNAPSHOT
4.12-SNAPSHOT
1.9.13-SNAPSHOT
20.0-SNAPSHOT

Это, однако, как можно видеть выше, дает мне только текст совпадений. То, что я хотел бы видеть, - это некоторый дополнительный контекст относительно местоположения совпадений, например, путь, ведущий к совпадающим элементам, например:

$ xmlstarlet magical arguments
/project/version: 0.2-SNAPSHOT
/project/dependencies/dependency: 4.12-SNAPSHOT
/project/properties/jackson.version: 1.9.13-SNAPSHOT
/project/properties/guava.version: 20.0-SNAPSHOT

В качестве альтернативы, урезанная версия XML в качестве вывода также будет работать для меня, например ::10000

$ xmlstarlet magical arguments
<project>
  <version>0.2-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <version>4.12-SNAPSHOT</version>
    </dependency>
  </dependencies>
  <properties>
    <jackson.version>1.9.13-SNAPSHOT</jackson.version>
    <guava.version>20.0-SNAPSHOT</guava.version>
  </properties>
</project>

Можно ли распечатать один из этих или какой-либо другой вид указания того, где произошли совпадения?

Ответы [ 3 ]

0 голосов
/ 09 января 2019

Я придумал следующее для создания урезанной версии XML:

xmlstarlet ed -d "//*[count((.|.//*)[contains(text(), 'SNAPSHOT')]) = 0]" pom.xml

Выход:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <version>0.2-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <version>4.12-SNAPSHOT</version>
    </dependency>
  </dependencies>
  <properties>
    <jackson.version>1.9.13-SNAPSHOT</jackson.version>
    <guava.version>20.0-SNAPSHOT</guava.version>
  </properties>
</project>

Идея состоит в том, чтобы удалить каждый узел, который не содержит текст SNAPSHOT и не имеет потомков, содержащих его. Мне не очень нравится, что я должен был использовать (.|.//*), чтобы соответствовать либо текущему узлу, либо его потомкам, должен быть лучший способ, но я обнаружил, что обычный .//* не соответствует текущему узлу, только его потомкам .

0 голосов
/ 08 марта 2019

xmlstarlet может выдавать запрошенный вывод, используя опцию -b, которая нарушает вложение:

xmlstarlet sel -t \
    -m "//*[contains(text(),'SNAPSHOT')]" \
    -m 'ancestor::*' -v 'name()' -o '/' \
    -b -v "concat(name(),': ',.)" -n pom.xml

Выход:

project/version: 0.2-SNAPSHOT
project/dependencies/dependency/version: 4.12-SNAPSHOT
project/properties/jackson.version: 1.9.13-SNAPSHOT
project/properties/guava.version: 20.0-SNAPSHOT
0 голосов
/ 08 января 2019

Мне не удалось получить желаемый результат с помощью инструмента выбора. Однако мне удалось изменить сгенерированный XSL в соответствии с вашими потребностями.

Я сгенерировал XSL с помощью переключателя -C:

xmlstarlet sel -C -t -m "//*[contains(text(), 'SNAPSHOT')]" -m 'ancestor-or-self::*' -v 'name()' -o / -n

Выход:

<?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="//*[contains(text(), 'SNAPSHOT')]">
      <xsl:for-each select="ancestor-or-self::*">
        <xsl:call-template name="value-of-template">
          <xsl:with-param name="select" select="name()"/>
        </xsl:call-template>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="'&#10;'"/>
      </xsl:for-each>
    </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>

Затем я применил следующий патч:

11d10
<         <xsl:value-of select="'&#10;'"/>
12a12,13
>       <xsl:value-of select="text()"/>
>       <xsl:value-of select="'&#10;'"/>

В результате:

<?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="//*[contains(text(), 'SNAPSHOT')]">
      <xsl:for-each select="ancestor-or-self::*">
        <xsl:call-template name="value-of-template">
          <xsl:with-param name="select" select="name()"/>
        </xsl:call-template>
        <xsl:text>/</xsl:text>
      </xsl:for-each>
      <xsl:value-of select="text()"/>
      <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>

Если вы примените это преобразование к исходному XML-файлу, вы получите желаемый результат:

xmlstarlet tr modified.xsl input.xml

Выход:

project/version/0.2-SNAPSHOT 
project/dependencies/dependency/version/4.12-SNAPSHOT
project/properties/jackson.version/1.9.13-SNAPSHOT
project/properties/guava.version/20.0-SNAPSHOT
...