Turn XML attribute with 2 or more values into SVG

2019-01-20 18:21发布

I need to turn the values in a xml attribute through xsl into a svg rect pos x and y

Therefore need to parse from

Examples

<item value="20 10 40" />
<item value="200 0 100" />
<item value="2666 10 40 95" />

parse each item value attribute following some rules (that's the part I'm not sure about, how to extract the numbers into separate variables)

like

 <item value="20 10 40" />
              X  Z  Y

Extract X (20) in var1 and Y (40) in var2

into

<rect x="{$var1}" y="{$var2}" />

(if I want the first and third value in this case)

Basically I need to understand how to parse any TWO of a series of multiple values contained within the attribute VALUE and pass them into the rect vars as var1 and var2 separately.

From my research so far, I have found out 2 methods but not sure how to apply in this case, either substring-before and after or tokenize, please note this needs to work loaded directly in a browser.

Edit1

I have found an ugly solution so far to extract the data for a case of 3 numbers

Xml

<item value="20 10 40" />

Xsl

Value 1    <xsl:value-of select="substring-before (@value, ' ')"/>
Value 2    <xsl:value-of select="substring-before(substring-after (@value, ' '), ' ')"/>
Value 3    <xsl:value-of select="substring-after(substring-after (@value, ' '), ' ')"/>

Result

Value 1    20
Value 2    10
Value 3    40

So looking for something cleaner, perhaps recursive that accept any amount of numbers in the string and parse all.

标签: xml xslt svg
2条回答
萌系小妹纸
2楼-- · 2019-01-20 19:08

Extracting values from a delimited list in XSLT 1.0 is awkward, since it has no tokenize() function.

If the number of values in the list is small (like in your example), you can use nested substring-before() and substring-after() calls, as shown (now) in your question.

A more generic solution, which is also more suitable to handle larger lists, would use a recursive named template, for example:

<xsl:template name="get-Nth-value">
    <xsl:param name="list"/>
    <xsl:param name="N"/>
    <xsl:param name="delimiter" select="' '"/>
    <xsl:choose>
        <xsl:when test="$N = 1">
            <xsl:value-of select="substring-before(concat($list, $delimiter), $delimiter)"/>
        </xsl:when>
        <xsl:when test="contains($list, $delimiter) and $N > 1">
            <!-- recursive call -->
            <xsl:call-template name="get-Nth-value">
                <xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
                <xsl:with-param name="N" select="$N - 1"/>
                <xsl:with-param name="delimiter" select="$delimiter"/>
            </xsl:call-template>
        </xsl:when>
    </xsl:choose>
</xsl:template>

Example of call:

<xsl:template match="item">
    <rect>
        <xsl:attribute name="x">
            <xsl:call-template name="get-Nth-value">
                <xsl:with-param name="list" select="@value"/>
                <xsl:with-param name="N" select="1"/>
            </xsl:call-template>                
        </xsl:attribute>
        <xsl:attribute name="y">
            <xsl:call-template name="get-Nth-value">
                <xsl:with-param name="list" select="@value"/>
                <xsl:with-param name="N" select="2"/>
            </xsl:call-template>                
        </xsl:attribute>
    </rect>
</xsl:template>

Demo:http://xsltransform.net/ncdD7mh

查看更多
放荡不羁爱自由
3楼-- · 2019-01-20 19:08

Here is an example using the EXSLT extensions, online at http://xsltransform.net/bnnZW2, the code is

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:exsl="http://exslt.org/common" xmlns:str="http://exslt.org/strings" exclude-result-prefixes="exsl str">

    <xsl:include href="http://exslt.org/str/functions/tokenize/str.tokenize.template.xsl"/>

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

    <xsl:template match="item">
      <xsl:copy>
        <xsl:variable name="tokens-rtf">
            <xsl:call-template name="str:tokenize">
                <xsl:with-param name="string" select="@value"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="tokens" select="exsl:node-set($tokens-rtf)/token"/>
        <x>
            <xsl:value-of select="$tokens[1]"/>
        </x> 
        <y>
            <xsl:value-of select="$tokens[2]"/>
        </y>
      </xsl:copy>

    </xsl:template>
</xsl:transform>

the input is

<root>
    <item value="20 10 40" />
    <item value="200 0 100" />
    <item value="2666 10 40 95" />
</root>

the output is

<root>
    <item><x>20</x><y>10</y></item>
    <item><x>200</x><y>0</y></item>
    <item><x>2666</x><y>10</y></item>
</root>

It should be clear how to access other tokens by the position, of course you can also for-each or apply-templates over $tokens.

Note that XSLT processors in browsers might not allow you to import or include a stylesheet module from a different domain, so you would to make sure you put the referenced stylesheet module (str.tokenize.template.xsl) on your own server and reference it from there.

查看更多
登录 后发表回答