Home > OS >  Transforming a Xml file using Xslt based on attribute value
Transforming a Xml file using Xslt based on attribute value

Time:12-09

Looking for a way to transform an xml using xlst based on predefined values

I am very new to xslt, please pardon me, if this is a basic question.

Input xml

<?xml version="1.0" encoding="UTF-8"?>
<manifest>

  <project groups="mygroup" name="test1/project1" path="current_dir" />
  <project groups="mygroup" name="test2/project2" path="some_dir/foo"/>
  <project groups="mygroup" name="test3/project3" path="new_dir/bar"/>
  <project groups="mygroup" name="test4/project4" path="current_dir/baz"/>

</manifest>  

Looking for a output

<?xml version="1.0" encoding="UTF-8"?>
    <manifest>
    
      <project groups="mygroup" name="test1/project1" path="myfolder" />
      <project groups="mygroup" name="test2/project2" path="myfolder/step1"/>
      <project groups="mygroup" name="test3/project3" path="test_folder/>      
    </manifest>  

Basically, change the path to a predefined custom path and exclude the lines that does not have a custom path.

if name="test1/project1" change path="myfolder"
if name="test2/project2" change path="myfolder/step1"
if name="test3/project3" change path="test_folder"

remove line for everything else

I started with the below code, but this one modifies path to same one ( considering that there are no conditional checks ). I would like to add conditions to check for "name" attribute, and change the "path" attribute to a corresponding path, and also drop for which a custom path is not needed. For example test4/project4 in the above example

<?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" indent="yes" encoding="utf-8"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
<xsl:template match="/manifest/project/@path">
    <xsl:attribute name="path">Custom Path</xsl:attribute>
</xsl:template>

</xsl:stylesheet>

CodePudding user response:

The rules you have posted are not clear. The output you show could be produced using the following stylesheet - but I am not certain that provides a true answer to your question.

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="project">
    <xsl:choose>
        <xsl:when test="@name='test1/project1'">
             <project groups="{@groups}" name="test1/project1" path="myfolder" />
        </xsl:when>
        <xsl:when test="@name='test2/project2'">
             <project groups="{@groups}" name="test2/project2" path="myfolder/step1" />
        </xsl:when>
        <xsl:when test="@name='test3/project3'">
             <project groups="{@groups}" name="test3/project3" path="test_folder" />
        </xsl:when>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Added:

Is it possible to change only the "path" attribute instead of repeating the other attributes ? I ask this because, there is a possibility that the input xml may get appended with other attributes over time

It is not possible to "change only the path attribute" because you also want to remove the parent project element altogether when none of the tests return true. However, it is possible to copy all the other attributes:

<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="project">
    <xsl:choose>
        <xsl:when test="@name='test1/project1'">
             <project path="myfolder">
                <xsl:copy-of select="@*[not(name()='path')] "/>
             </project>
        </xsl:when>
        <xsl:when test="@name='test2/project2'">
             <project path="myfolder/step1">
                <xsl:copy-of select="@*[not(name()='path')] "/>
             </project>
        </xsl:when>
        <xsl:when test="@name='test3/project3'">
             <project path="test_folder" >
                <xsl:copy-of select="@*[not(name()='path')] "/>
             </project>
        </xsl:when>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

CodePudding user response:

You just have to run through the "project" nodes and check the corresponding queries via "if" and write the change if there is a hit.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="#all" version="2.0">
  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:template match="/">
     <manifest>
          <xsl:for-each select="//manifest/project">
            <xsl:choose>
               <xsl:when test="@name='test1/project1'">
                    <project path="myfolder">
                         <xsl:copy-of select="@*[not(name()='path')]"/>
                    </project>
               </xsl:when>
               <xsl:when test="@name='test2/project2'">
                    <project path="myfolder/step1">
                         <xsl:copy-of select="@*[not(name()='path')]"/>
                    </project>
               </xsl:when>
               <xsl:when test="@name='test3/project3'">
                    <project path="test_folder">
                         <xsl:copy-of select="@*[not(name()='path')]"/>
                    </project>
               </xsl:when>
               <xsl:otherwise>
                    <!-- Otherwise To Nothing -->
               </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each>
     </manifest>
  </xsl:template>
</xsl:stylesheet>

Also have a look at the reference:

  • Related