Wojciech Sobczyk
Wojciech Sobczyk

Reputation: 343

XSLT - copy listed nodes

I want to generate translation files that will hide some parameters based on different software version and customer. My other requirement is to sort the Param based on a generated list.

EDIT: I don't want to prepare a list of excluded nodes as the original set is dynamic and only the specified nodes if found should be copied.

Example source XML

<?xml version="1.0" ?>
<ns:ParameterElements AllowAdd="True" moreattributes xmlns:ns="http://www.somens.com/schema">
<ParameterFile>
    <Container>
        <Param Name="n1">
            ...
        </Param>
        <Param Name="sth1">
            ...
        </Param>
        <Param Name="toberemoved" ...>
            ...
        </Param>
    </Container>
</ParameterFile>
</ns:ParameterElements>

I did try plenty approach but this one seems to be closest XSL Copy Nodes based on Attribute Value (Like Search)

<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="*"/>

<xsl:param name="paramSortingList" select="('sth1','n1')"/>

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

<xsl:template match="ParameterFile/Container/Param">
    <xsl:copy>
        <xsl:copy-of select="@*[name()=$paramSortingList]|node()"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

So this would be perfect for me as with one list I could copy the nodes in the order I need.

Unfortunately the presented solution does not work,

EDIT: my desired outcome:

<?xml version="1.0" ?>
<ns:ParameterElements AllowAdd="True" moreattributes xmlns:ns="http://www.somens.com/schema">
<ParameterFile>
    <Container>
        <Param Name="sth1">
            ...
        </Param>
        <Param Name="n1">
            ...
        </Param>
    </Container>
</ParameterFile>
</ns:ParameterElements>

please note I don't want to prepare a list of excluded nodes as the set is dynamic and only the specified nodes if found should be copied.

Upvotes: 0

Views: 307

Answers (2)

Tim C
Tim C

Reputation: 70598

Using an XSLT 2.0 is the way to go here, as has been answered already, but if were interested in an XSLT 1.0 solution in the short-term, here is how you would do it (using some crude string manipulation)

<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="*"/>

<xsl:param name="paramSortingList" select="'sth1,n1'"/>
<xsl:param name="paramSortingListPlus" select="concat(',', $paramSortingList, ',')"/>

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

<xsl:template match="ParameterFile/Container">
    <xsl:copy>
      <xsl:apply-templates select="Param[contains($paramSortingListPlus, concat(',', @Name, ','))]">
        <xsl:sort select="string-length(substring-before($paramSortingListPlus, concat(',', @Name, ',')))" />
      </xsl:apply-templates>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167471

Assuming at least XSLT 2.0 you can simply use

<xsl:template match="ParameterFile/Container/Param[not(@Name = $paramSortingList)]"/>

in your stylesheet to ensure the Param elements not in your name sequence are not copied.

If you move to XSLT 3.0 as supported by Saxon 9.8 or Altova 2017 or 2018 you can shorten the whole to

<?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"
    exclude-result-prefixes="xs"
    version="3.0">

    <xsl:param name="paramSortingList" select="('sth1','n1')"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="ParameterFile/Container/Param[not(@Name = $paramSortingList)]"/>

</xsl:stylesheet>

Upvotes: 1

Related Questions