0

I'm writing an XSLT that, among other things, locates certain elements that have a "name" attribute with a value that starts with a dash ('-'). When such attribute is found, the xslt creates an xsl:attribute, which name is all the text that comes after the "name" attribute's dash.

So, for instance, suppose I have the following XML segment:

<someelement>
    <json:string name="-style">display: block; white-space: pre; border: 2px     
                       solid #c77; padding: 0 1em 0 1em; margin: 1em; 
                        background-color: #fdd; color: black</json:string>
                     [... some extra elements here ...]
</someelement>

And I want it to become

<someelement style="display: block; white-space: pre; border: 2px solid #c77; 
                   padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; 
                   color: black">
                 [... some extra elements here ...]
</someelement>

Currenty, I'm trying several variations on the following XSLT:

  <!-- Match string|number|boolean|null elements with a "name" attribute -->
  <xsl:template match="json:string[@name] | json:number[@name] | json:boolean[@name] | json:null[@name]">
    <xsl:choose>
      <xsl:when test="starts-with(@name,'-')">
        <xsl:attribute name="{substring(./[@name],2,string-length(./@name) - 1)}">
          <xsl:value-of select="."/>
        </xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="{@name}">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

And to be specific, this is the line that puzzles me:

<xsl:attribute name="{substring(./[@name],2,string-length(./@name) - 1)}">

To be even more specific, the part in it which doesn't work is the string-length part. If I replace the entire string-length part with a number, say 2, it works just fine.

And yes, I know that substring-after would suite me better. I did try the following, but it doesn't work either:

<xsl:attribute name="{substring-after(./[@name],'-')}">

I'm sure this is some kind of syntactic error.

P.S. - I'm using XMLSPY for my tests.

Your help is very much appreciated.

dotaneli
  • 45
  • 11

1 Answers1

0

I would suggest you try it this way:

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="*[starts-with(@name, '-')]">
    <xsl:attribute name="{substring(@name, 2)}">
        <xsl:value-of select="."/>
    </xsl:attribute>    
</xsl:template>

</xsl:stylesheet>

Test: http://xsltransform.net/6r5Gh3g

Note that this will fail if these "special" elements are not first to be processed: http://xsltransform.net/6r5Gh3g/1 If this is a possibility, you must also match the parent element and control the order of processing from there:

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="*[starts-with(@name, '-')]">
    <xsl:attribute name="{substring(@name, 2)}">
        <xsl:value-of select="."/>
    </xsl:attribute>    
</xsl:template>

<xsl:template match="*[*[starts-with(@name, '-')]]">
    <xsl:copy>
        <xsl:apply-templates select="*[starts-with(@name, '-')]"/>
        <xsl:apply-templates select="@*|node()[not(starts-with(@name, '-'))]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

http://xsltransform.net/6r5Gh3g/3

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks, but I do want to have the string-length perform well inside the substring. How can I make that work? : – dotaneli Nov 29 '15 at 12:25
  • @dotaneli It is not necessary to use `string-length()` here. See the definition of the substring() function: http://www.w3.org/TR/xpath/#function-substring – michael.hor257k Nov 29 '15 at 13:13
  • Thanks, you are ofcourse correct. But if I trey that: , I get this from XMLSPY: XSLT 2.0 Debugging Error: Illegal attribute name. Current XML node is 'http://www.google.com'. – dotaneli Nov 29 '15 at 13:43
  • @dotaneli That's another issue. `xmlns` is not a valid attribute name, because the string is reserved for declaring a namespace. In fact, no name in XML should start with "xml": http://www.w3.org/TR/xml/#sec-common-syn – michael.hor257k Nov 29 '15 at 14:08
  • P.S. It looks like you are trying to reconstruct some original XML document from a badly transformed mutation. If so, it would be better to address the problem at its source. – michael.hor257k Nov 29 '15 at 14:33
  • Thank you, @michael.hor257k. Indeed it seems that since my XML had "-xmlns" attributes in couldn't be parsed. Regarding your note about the bad transformation, you are indeed correct. I can't, however, control that structure. It is given to me as is. – dotaneli Nov 30 '15 at 05:39
  • 1
    @dotaneli You may have bigger issues than you think. `xmlns` is a *namespace declaration*, not an *attribute*. It needs to be handled differently. Moreover, a default namespace is inherited by the descendant elements - so you need to be careful how you construct those too, without breaking the inheritance. – michael.hor257k Nov 30 '15 at 05:51
  • Right again, thanks. Handling elements with the name "-xml" is not that hard since I can use xsl:choose to filter those out and create a namespace instead of an attribute. – dotaneli Nov 30 '15 at 06:25
  • As I said, I suspect it may not be as easy as that - but I don't see the entire picture. In any case, I believe your current question has been answered? – michael.hor257k Nov 30 '15 at 12:11
  • @dotaneli Was it not? – michael.hor257k Dec 02 '15 at 22:51
  • This was indeed the issue. Thank you so much, @michael.hor257k – dotaneli Dec 07 '15 at 11:53
  • turns out the problem was the "xmlns" "attirubte", rather than the original xsl code. But your solution above did not focus on that. I want to accept the relevant answer, but it's in your comment. – dotaneli Dec 10 '15 at 10:58