简体   繁体   中英

Convert XML tags to attributes in arbitrary locations with XSLT

We have an XML format that is actually just HTML with some extra gubbins. It defines which HTML elements are editable, and which attributes are editable thereof.

Here's an example

<img src="images/placeholder.jpg" 
     alt="Placeholder" 
     width="600" 
     height="250" 
     border="0">
  <var attr="src" />
  <var attr="height" ok="150-300" />
</img>

The XML is so the templates are easier to write but I need to convert it to valid(ish) HTML. To do this I want to collapse it into:

<img src="images/placeholder.jpg" 
     alt="Placeholder" 
     width="600" 
     height="250" 
     border="0"
     editable="src height" 
     constraints="height:150-300">

I can do this for <img> , but the problem is that the <var> tags can actually appear as a child of any element in the page. In all cases the algorithm for converting it to attributes is the same but what I can't work out how to do is specify an XSLT template that can match the parent element of a <var> tag. I tried match="var/.." but this turned out to be invalid.

The alternative would be to match the <var> s and add them as attributes to their parents, but I believe that at this point in the processing that particular ship would already have sailed.

Is this doable?

Thankies

Answer!

I combined the more complete answer with Dimitre's usefully accurate answer to form the following:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="*[var]">
        <xsl:copy >
            <xsl:attribute name="editable">
                <xsl:for-each select="var[@attr]">
                    <xsl:value-of
                        select="concat(@attr,
                            substring(' ',
                                1 div (position()!=last())))"/>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:attribute name="constraints">
                <xsl:for-each select="var[@ok]">
                    <xsl:value-of
                        select="concat(@attr,':',@ok,
                            substring(';',
                                1 div (position()!=last())))"/>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="var">
    </xsl:template>

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

Unfortunately the exact answer Alejandro gave didn't work straight away (still not sure why),but this combination of his answer and Dmitri's answer seems to have done the job nicely :)

what I can't work out how to do is specify an XSLT template that can match the parent element of a <var> tag

Use :

<xsl:template match="*[var]">

我将考虑在解析器到达<img>标记并在该变量内显式构造一个新的<img ...> ,构造一个局部变量( <xsl:variable> ),遍历它找到的所有<var>子元素( <xsl:foreach ...>有人吗?)在耗尽后关闭变量...然后将变量写出来?

Besides Dimitre's exact answer to your question, other approach would be:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|var"/>
            <xsl:apply-templates select="node()[not(self::var)]"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="var"/>
    <xsl:template match="var[1]">
        <xsl:attribute name="editable">
            <xsl:for-each select="../var/@attr">
                <xsl:value-of
                     select="concat(.,
                                    substring(' ',
                                              1 div (position()!=last())))"/>
            </xsl:for-each>
        </xsl:attribute>
        <xsl:attribute name="constraints">
            <xsl:for-each select="../var/@ok">
                <xsl:value-of
                     select="concat(../@attr,':',.,
                                    substring(';',
                                              1 div (position()!=last())))"/>
            </xsl:for-each>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

Output:

<img src="images/placeholder.jpg" 
     alt="Placeholder" 
     width="600" 
     height="250" 
     border="0" 
     editable="src height" 
     constraints="height:150-300"></img>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM