AAlred
AAlred

Reputation: 85

Find an XSL node by one child value, then change another child value in that node

So I know how to find a node based upon one of it's child values, but how would I go about finding an xml node by one child value, then changing another child value of that node accordingly. i.e. if I wanted to find a dog node by it's name tag, then change it's breed tag.

<dog>
    <name>Fido</name>
    <breed>GSD</breed>
</dog>

should become:

<dog>
    <name>Fido</name>
    <breed>Labrador</breed>
</dog>

Thanks, Adam

Upvotes: 0

Views: 849

Answers (3)

AAlred
AAlred

Reputation: 85

So from taking Daniel's answer and running with it, this is ultimately what gave me the best solution. (I am paraphrasing the code for obvious reasons but it should suffice). I am still saying that Daniel's answer will work because in most situations it will be fine but I ended up needing a way to handle additional options that I was unaware of when creating the question.

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


    <xsl:template match="../dog[name = $dog.variable.name]:>
        <xsl:copy-of select="."/>
        <xsl:copy-of select="optionalParams"/>
        .
        .
        .
        <breed>Labrador</breed>
    </xsl:template>

Upvotes: 0

Daniel Haley
Daniel Haley

Reputation: 52888

Normally you could just match the breed in the dog with the name "Fido" like this...

<xsl:template match="dog[name='Fido']/breed">
  <breed>Labrador</breed>
</xsl:template>

But you said in a comment:

how would it handle doing that if say the name was a variable passed in through xsltproc?

In XSLT 1.0 you can't reference a variable or parameter in a match statement. You would need to use an xsl:choose to check the name (updated to also pass the breed in as an xsl:param, but it's not strictly necessary)...

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

  <xsl:param name="animalName" select="'Fido'"/>
  <xsl:param name="animalBreed" select="'Labrador'"/>

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

  <xsl:template match="breed">
    <xsl:choose>
      <xsl:when test="../name=$animalName">
        <breed><xsl:value-of select="$animalBreed"/></breed>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

In XSLT 2.0 you could just change the match to dog[name=$animalName]/breed.

Upvotes: 1

zx485
zx485

Reputation: 29052

You can use an identity template and a replacement template replacing specific <breed> elements:

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

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

  <!-- replaces <breed> elements with specific text content --> 
  <xsl:template match="breed[text()='GSD' and parent::dog[name='Fido']]">
    <breed>Labrador</breed>
  </xsl:template>

</xsl:stylesheet>

Output:

<dog>
    <name>Fido</name>
    <breed>Labrador</breed>
</dog>

Upvotes: 1

Related Questions