Я хотел бы заменить элемент в XML (большой) несколькими элементами, как показано ниже:

Оригинальный XML:

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>
</root>

Я бы хотел, чтобы результат был:

<root>
  <cr>
   <id>1</id>
   <release>Aa</release>
   <release>Ab</release>
   <release>Ad</release>
  </cr>
  <cr>
   <id>2</id>
   <release>Bd</release>
   <release>Be</release>
  </cr>
</root>

Принцип состоит в том, что, когда есть // release [text () = 'A'], замените элемент тремя выше, когда есть // release [text () = 'B'], замените элемент двумя выше и т. Д. • Если текст выпуска () - «C» или «D» или другие значения, они остаются теми же значениями.

Моя попытка:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="//cr/release/text()">
    <xsl:if test=".='A'">
        <xsl:value-of select="Aa"/>
    </xsl:if>
</xsl:template>
</xsl:stylesheet>

Он работает с одной заменой на одну, но как сделать несколько? Большое спасибо,

1
dellair 31 Дек 2015 в 16:41

2 ответа

Лучший ответ

Вот полностью общее и короткое решение XSLT 1.0 . Он использует XML-файл сопоставления , в котором указываются замены для каждого желаемого выпуска:

(Пожалуйста, найдите в конце этого ответа краткое и общее решение, написанное на XSLT 2.0.)

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

Если файл mapping.xml находится в том же каталоге, что и преобразование (файл .xsl), и:

<map>
    <release old="A">
        <release>Aa</release>
        <release>Ab</release>
        <release>Ad</release>
    </release>
    <release old="B">
        <release>Ba</release>
        <release>Bb</release>
        <release>Bd</release>
    </release>
</map>

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

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>
  <cr>
   <id>3</id>
   <release>C</release>
  </cr>
  <cr>
   <id>4</id>
   <release>D</release>
  </cr>
</root>

желаемый, правильный результат :

<root>
   <cr>
      <id>1</id>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <release>Ba</release>
      <release>Bb</release>
      <release>Bd</release>
   </cr>
   <cr>
      <id>3</id>
      <release>C</release>
   </cr>
   <cr>
      <id>4</id>
      <release>D</release>
   </cr>
</root>

Примечание . Многие элементы с разными названиями могут быть заменены одним и тем же преобразованием.

Если у нас есть этот файл сопоставления :

<map>
    <release old="A">
        <release>Aa</release>
        <release>Ab</release>
        <release>Ad</release>
    </release>
    <release old="B">
        <release>Ba</release>
        <release>Bb</release>
        <release>Bd</release>
    </release>
    <history old="p">
        <history>Pp</history>
        <history>Pq</history>
        <history>Pr</history>
    </history>
</map>

и этот исходный XML-документ :

<root>
  <cr>
   <id>1</id>
   <history>p</history>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <history>q</history>
   <release>B</release>
  </cr>
  <cr>
   <id>3</id>
   <history>r</history>
   <release>C</release>
  </cr>
  <cr>
   <id>4</id>
   <history>t</history>
   <release>D</release>
  </cr>
</root>

затем это преобразование :

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
  <xsl:template match="history[. = document('mapping.xml')/*/history/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

при применении к вышеупомянутому XML-документу дает желаемый результат - где более одного элемента с разными именами отображаются и заменяются:

<root>
   <cr>
      <id>1</id>
      <history>Pp</history>
      <history>Pq</history>
      <history>Pr</history>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <history>q</history>
      <release>Ba</release>
      <release>Bb</release>
      <release>Bd</release>
   </cr>
   <cr>
      <id>3</id>
      <history>r</history>
      <release>C</release>
   </cr>
   <cr>
      <id>4</id>
      <history>t</history>
      <release>D</release>
   </cr>
</root>

И наконец: в XSLT 2.0 можно написать еще более короткое и общее преобразование :

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match=
   "*[. = $vMap[name() eq name(current())]/@old]">
    <xsl:copy-of select="$vMap[name() eq name(current()) and @old eq current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

Обратите внимание : в этом преобразовании жестко не задано имя элемента!

Еще большее преимущество : мы можем изменить преобразование, чтобы мы могли передать URI документа сопоставления в качестве глобального параметра при вызове преобразования - таким образом, мы можем иметь одно общее преобразование, которое работает с любое, заранее неизвестное картографирование.

Требуются только следующие изменения :

 <xsl:param name="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>

 <xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>

Полное преобразование (глобальный параметр pmapUrl может и обычно будет указываться динамически инициатором преобразования):

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>

 <xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match=
   "*[for $name in name() 
        return
          . = $vMap[name() eq $name]/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>
2
Dimitre Novatchev 4 Янв 2016 в 15:20

Попробуйте так:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="release[.='A']">
    <release>Aa</release>
    <release>Ab</release>
    <release>Ad</release>
</xsl:template>

<xsl:template match="release[.='B']">
    <release>Bd</release>
    <release>Be</release>
</xsl:template>

</xsl:stylesheet>

Тестовый ввод

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>  
  <cr>
   <id>3</id>
   <release>C</release>
  </cr>
</root>

Результат

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <cr>
      <id>1</id>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <release>Bd</release>
      <release>Be</release>
   </cr>
   <cr>
      <id>3</id>
      <release>C</release>
   </cr>
</root>
2
michael.hor257k 31 Дек 2015 в 14:05