Использование XSLT для переноса элементов, но не включая последний соответствующий элемент

Я пытаюсь преобразовать старые файлы XML в новую улучшенную структуру. Частично мне нужно, чтобы я обернул некоторые свободные элементы в родительский контейнер, а также изменил их дочерние элементы.

Старая структура

<monograph>
  <title>asdf</title>

  <dosage.sec id="dosage.sec.1">
    <dosage.sec>asgfd</dosage.sec>
    <dosage.sec>asgfd</dosage.sec>
  </dosage.sec>
  <dosage.sec id="dosage.sec.2">
    <dosage.sec>asgfd</dosage.sec>
    <dosage.sec>asgfd</dosage.sec>
  </dosage.sec>
  <dosage.sec id="dosage.sec.3">
    <dosage.sec>asgfd</dosage.sec>
    <dosage.sec>asgfd</dosage.sec>
  </dosage.sec>

  <products>
    <prod>sadf</prod>
    <prod>sadf</prod>
  </products>
</monograph>

Новая структура

<monograph>
  <title>asdf</title>

  <dosage>
    <dosage.sec id="dosage.sec.1">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec id="dosage.sec.2">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec id="dosage.sec.3">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
  </dosage>

  <products>
    <prod>sadf</prod>
    <prod>sadf</prod>
  </products>
</monograph>

Я нашел этот ответ и изменил это немного соответствует моим потребностям:

<!-- wrap dosage.sec elements in a dosage container -->
<xsl:template match="node()|@*" name="dosage.sec">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
</xsl:template>

<xsl:template match="monograph/dosage.sec[not(preceding-sibling::*[1][self::dosage.sec])]">
    <dosage>
        <xsl:call-template name="dosage.sec" />
        <xsl:apply-templates mode="copy" select="following-sibling::*[1][self::dosage.sec]" />
    </dosage>
</xsl:template>

<xsl:template match="monograph/dosage.sec" mode="copy">
    <xsl:call-template name="dosage.sec"/>
</xsl:template>


<!-- rename children dosage.sec -->
<xsl:template match="dosage.sec/dosage.sec">
    <dosage.qual>
        <xsl:apply-templates />
    </dosage.qual>
</xsl:template>

Но мой вывод:

<monograph>
  <title>asdf</title>

  <dosage>
    <dosage.sec id="dosage.sec.1">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec id="dosage.sec.2">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec id="dosage.sec.3">
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
  </dosage>

  <dosage.sec id="dosage.sec.3">
    <dosage.qual>asgfd</dosage.qual>
    <dosage.qual>asgfd</dosage.qual>
  </dosage.sec>

  <products>
    <prod>sadf</prod>
    <prod>sadf</prod>
  </products>
</monograph>

Я использую встроенный в PHP5 объект XSLTProcessor — все XML и XSL имеют версию 1.0.


person HorusKol    schedule 05.03.2013    source источник
comment
Ваш XSLT, вероятно, будет работать, если вы удалите [1] из этой строки: <xsl:apply-templates mode="copy" select="following-sibling::*[1][self::dosage.sec]" />, но то, как вы это делаете, может быть не лучшим подходом.   -  person JLRishe    schedule 05.03.2013
comment
Ответ, на который вы ссылались, касался конкретного сценария, когда им нужно было обернуть элементы, у которых были разные элементы до и после них. У вас есть такое требование, или это так же просто, как ваш пример?   -  person JLRishe    schedule 05.03.2013
comment
Все элементы Doze.sec должны быть смежными в пределах исходного документа (никаких других элементов между ними), так это проще. Я изменил свой пример, так как понял, что неточно изобразил, что у меня есть другие элементы в монографии до и после блока дозировки.   -  person HorusKol    schedule 06.03.2013


Ответы (2)


Как насчет этого:

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

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

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

  <!-- rename children dosage.sec -->
  <xsl:template match="dosage.sec/dosage.sec">
    <dosage.qual>
      <xsl:apply-templates />
    </dosage.qual>
  </xsl:template>
</xsl:stylesheet>

При запуске на вашем образце ввода это производит:

<monograph>
  <dosage>
    <dosage.sec>
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec>
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
    <dosage.sec>
      <dosage.qual>asgfd</dosage.qual>
      <dosage.qual>asgfd</dosage.qual>
    </dosage.sec>
  </dosage>
</monograph>
person JLRishe    schedule 05.03.2013

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

Эти шаблоны решили мои проблемы:

<!-- wrap dosage.sec elements in a dosage container -->
<xsl:template match="monograph/dosage.sec" name="dosage.sec">
    <xsl:param name="drugname" />

    <xsl:copy>
        <xsl:apply-templates>
            <xsl:with-param name="drugname" select="$drugname" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

<xsl:template match="monograph/dosage.sec[not(preceding::dosage.sec)]">
    <xsl:param name="drugname" />

    <dosage>
        <xsl:call-template name="dosage.sec">
            <xsl:with-param name="drugname" select="$drugname" />
        </xsl:call-template>

        <xsl:apply-templates mode="copy" select="following::dosage.sec">
            <xsl:with-param name="drugname" select="$drugname" />
        </xsl:apply-templates>
    </dosage>
</xsl:template>

<xsl:template match="monograph/dosage.sec" mode="copy">
    <xsl:param name="drugname" />

    <xsl:call-template name="dosage.sec">
        <xsl:with-param name="drugname" select="$drugname" />
    </xsl:call-template>
</xsl:template>

<xsl:template match="monograph/dosage.sec[(preceding::dosage.sec)]" />
person HorusKol    schedule 05.03.2013