Question

I'm trying to remove a node name, but keep the contents, or copy the contents of a node to it's parent node. I'm exporting XML from SSRS and have attached an XSLT file to the report RDL. I feel like I'm sooo close with the xslt I have. I'm trying to remove node 'Cell' which is repeated frequently in the file.

I want this:

<ReportSectionAPercentLabel>
    <Cell>
        <losPct>0.262158054711246</losPct>
    </Cell>
</ReportSectionAPercentLabel>

To look like this:

<ReportSectionAPercentLabel>
    <losPct>0.262158054711246</losPct>
</ReportSectionAPercentLabel>

This is an excert from the XML:

<?xml version="1.0" encoding="UTF8"?>
<Report xmlns="My_Report" Name="My report">
<ReportSectionATablix>
    <ReportSectionARowGroup_Collection>
        <ReportSectionARowGroup>
            <losProvider>Your Enterprise</losProvider>
            <ReportSectionAColumnGroup_Collection>
                <ReportSectionAColumnGroup>
                    <ReportSectionAGroupLabel>07</ReportSectionAGroupLabel>
                    <ReportSectionACountLabel>
                        <Cell>
                            <ReportSectionACount>345</ReportSectionACount>
                        </Cell>
                    </ReportSectionACountLabel>
                    <ReportSectionAPercentLabel>
                        <Cell>
                            <losPct>0.262158054711246</losPct>
                        </Cell>
                    </ReportSectionAPercentLabel>
                </ReportSectionAColumnGroup>
                <ReportSectionAColumnGroup>
                    <ReportSectionAGroupLabel>814</ReportSectionAGroupLabel>
                    <ReportSectionACountLabel>
                        <Cell>
                            <ReportSectionACount>153</ReportSectionACount>
                        </Cell>
                </ReportSectionACountLabel>
                ...

This is the XSLT. Do I have the Xpath to Cell wrong?

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

<xsl:template match="Cell" >
   <xsl:apply-templates select="*" />
</xsl:template>

Was it helpful?

Solution

Your approach is the correct one, but the issue is one of namespaces. In your XML, you have a default namespace declaration (the xmlns)

<Report xmlns="My_Report" Name="My report">

This means that element, and all descendants, belong to that namespace (unless overridden by another namespace declaration). However, in your XSLT, where you have specified Cell as the template match, this is looking for a Cell element in NO namespace, which won't match your Cell in the XML with a namespace.

The solution is to declare the namespace in the XSLT too, with a prefix, and use that prefix in matching the Cell element:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:rep="My_Report">
   <xsl:template match="node()|@*" >
      <xsl:copy>
         <xsl:apply-templates select="node()|@*" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="rep:Cell" >
      <xsl:apply-templates select="*" />
   </xsl:template>
</xsl:stylesheet>

Alternatively, if you were using XSLT 2.0, you could use the xpath-default-namespace, in which case any Xpath expression without a namespace prefix, is assumed to be in that default namespace

This would also work...

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xpath-default-namespace="My_Report">
   <xsl:template match="node()|@*" >
      <xsl:copy>
         <xsl:apply-templates select="node()|@*" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="Cell" >
      <xsl:apply-templates select="*" />
   </xsl:template>
</xsl:stylesheet>

If, for whatever reason, you wanted to avoid coding the namespace declaration in your XSLT, you could change the template match to the following:

<xsl:template match="*[local-name()='Cell']" >
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top