0

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>
10
  • While asking an XSLT question you need to provide a minimal reproducible example: (1) Input XML. (2) Your logic, and XSLT that tries to implement it. (3) Desired output, based on the sample XML in the #1 above. (4) XSLT processor and its compliance with the XSLT standards: 1.0, 2.0, or 3.0. Commented Dec 8, 2021 at 18:57
  • Please edit your question and add the exact rules the stylesheet should follow when modifying the value of the path attribute. Commented Dec 8, 2021 at 19:12
  • @yitzhak-khabinsky I am stuck at matching the exact attribute, checking this further will update the question, if I get more ideas on improving the example. Commented Dec 8, 2021 at 19:26
  • I am using xsltproc tool for the conversion, I think any XSLT standard may work. Commented Dec 8, 2021 at 19:30
  • @michael.hor257k added some edits, does it address your comment ? Commented Dec 8, 2021 at 19:32

2 Answers 2

1

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>
Sign up to request clarification or add additional context in comments.

3 Comments

Perfect.!! This is exactly what I was looking for. Thank you. 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
See the addition to my answer.
@SmartCoder Wrong page?
0

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:

4 Comments

Thank you, @andreas-hauser. Yes. This solution worked too. As in my other comment, which I had not made it clear in the question.. is it possible to just change the "path" attribute after matching and keep all other attributes in the line intact? There is a possibility of additional attributes to be added in the future
Of course, therefore you just need to take the current "project"-node in the if-condition and change only the value form the "path"-attribute.
For mutually exclusive alternatives, xsl:choose is preferred over multiple xsl:if instructions. It saves redundant evaluations after a true case has been found.
@michael.hor257k you are right, especially when large amounts of data have to be processed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.