简体   繁体   中英

Howto move an element in xml only when a value inside element matches with xslt?

This one I'm struggling with for a few day's now. I can solve little parts of the puzzle but when trying to merge it into one xslt stylesheet I'm completely lost.

The source xml looks like this (simplified)

<tr>
  <pic>
    <hs>
      <spot>
        <posit>
          <pan>28</pan>
          <tilt>44</tilt>
        </posit>
        <id>_flare</id>
      </spot>
      <spot>
        <posit>
          <pan>6</pan>
          <tilt>7</tilt>
        </posit>
      </spot>
      <spot>
        <posit>
          <pan>4</pan>
          <tilt>8</tilt>
        </posit>
        <id>Point01</id>
      </spot>
    </hs>
    <snd>
      <level>1</level>
      <sound>
        <loop>1</loop>
      </sound>
    </snd>
  </pic>
</tr>

The expected output should look like this:

<tr>
  <pic>
    <hs>
      <spot>
        <posit>
          <pan>6</pan>
          <tilt>7</tilt>
        </posit>
      </spot>
      <spot>
        <posit>
          <pan>4</pan>
          <tilt>8</tilt>
        </posit>
        <id>Point01</id>
      </spot>
    </hs>
    <snd>
      <level>1</level>
      <sound>
        <loop>1</loop>
      </sound>
      <lf>
          <pos>
            <pan>28</pan>
            <tilt>44</tilt>
          </pos>
          <col>#ffffff</col>
      </lf>
    </snd>
  </pic>
</tr>

I think it needs some explanation. Only the "spot" with an "id" that matches "_flare" in "hs" should be moved and added to the "snd" element. It also has to be reformatted from:

  <spot>
    <posit>
      <pan>28</pan>
      <tilt>44</tilt>
    </posit>
    <id>_flare</id>
  </spot>

to

  <lf>
      <pos>
        <pan>28</pan>
        <tilt>44</tilt>
      </pos>
      <col>#ffffff</col>
  </lf>

I appreciate a hint in the right direction to get me going again !

Regards,

AHG

How about:

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="spot[id='_flare']"/>

<xsl:template match="snd">
    <xsl:copy>
        <xsl:apply-templates/>
        <xsl:apply-templates select="../hs/spot[id='_flare']" mode="add"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="spot" mode="add">
    <lf>
        <pos>
            <xsl:copy-of select="posit/pan | posit/tilt"/>
        </pos>
        <col>#ffffff</col>
    </lf>
</xsl:template>

</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output encoding="UTF-8" method="xml" version="1.0" indent="yes"/>
    <!-- Catch-all templates -->
    <xsl:template match="@*|text()">
        <xsl:copy-of select="."/>
    </xsl:template>
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:template>
    <xsl:template match="processing-instruction()">
        <xsl:copy/>
    </xsl:template>
    <!-- delete spot from the top -->
    <xsl:template match="spot[id[text()='_flare']]"/>
    <!-- add spot to the bottom -->
    <xsl:template match="snd">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <xsl:apply-templates select="//spot[id[text()='_flare']]" mode="addToBottom"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="spot[id[text()='_flare']]" mode="addToBottom">
        <lf>
            <pos>
                <xsl:apply-templates select="./posit/*"/>
            </pos>
            <col>#ffffff</col>
        </lf>
    </xsl:template>
</xsl:stylesheet>

I found the answer myself on the last question.

My xslt stylesheet is now:

<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="spot[id='_flare']"/>

<xsl:template match="snd">
    <xsl:copy>
        <xsl:apply-templates/>
        <xsl:apply-templates select="../hs/spot[id='_flare']" mode="add"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="spot" mode="add">
    <lf>
        <pos>
            <xsl:copy-of select="posit/pan | posit/tilt"/>
        </pos>
        <col>#ffffff</col>
    </lf>
</xsl:template>

<xsl:template match="id"/>

<xsl:template match="hs/spot" >
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
<id> <xsl:value-of select="concat('Point',count(preceding::spot)+1)"/> </id>
</xsl:element>
</xsl:template>


</xsl:stylesheet>

There might be a smarter way though ....

Based on michael.hor257k's solution, you can leave it unchanged and simply add one more template for handling the id's:

<xsl:template match="spot">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()[local-name()!='id']"/>
        <id>
            <xsl:text>Point</xsl:text>
            <xsl:value-of select="format-number(count(preceding-sibling::spot[not(id='_flare')])+1,'00')"/>
        </id>
    </xsl:copy>
</xsl:template>

The old id's will be ignored, and each element gets a new one, starting from "Point01".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM