如何使用XSL将XML文件转换为SVG?

11

给定

<root>
   <item>
      <detail>100</detail>
      <detail>200</detail>
   </item>
   <item>
      <detail>50</detail>
      <detail>100</detail>
   </item>
</root>
我该如何将这些数据制作成简单的SVG柱状图呢?(没有什么花哨的东西,只是四个条形图以某种方式表示数字之间的关系)
类似于这样: enter image description here (我知道这两个项目之间没有分隔符,但是假设我会使它们变成不同的颜色,前两个条形图为蓝色,后两个为红色)
我想我不确定xsl:template内部的语法应该如何生成SVG代码?最佳答案将被接受!

1
请提供所需输出的示例,除非您正在寻找同时了解SVG和XSLT的人,这可能是一个狭窄的领域,祝您好运。 ;) - Tomalak
这个网站可能是个不错的起点:http://www.carto.net/svg/samples/xslt/ - david
1
@antonpug:我所说的“示例输出”是指实际的SVG代码,而不是图像。 - Tomalak
1
你的问题是什么:决定SVG应该长成什么样子,还是从你的XML输入生成SVG?换句话说,你是在问一个SVG问题还是一个XSLT问题? - Michael Kay
我不知道如何使用XSL生成SVG代码,例如如何设置模板。 - antonpug
2个回答

14

以下是一个带有更多功能的示例:

  • 它可以自动缩放到最大高度
  • 它使用CSS样式元素
  • 它具有可配置的参数,用于设置条形图的宽度和间距
  • 它使用比嵌套的for-each循环更典型的XSLT

使用您的输入,此代码如下:

<xsl:stylesheet
   version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
   xmlns="http://www.w3.org/2000/svg"
>
  <xsl:output indent="yes" cdata-section-elements="style" />

  <xsl:param name="width" select="40" /><!-- width of bars -->
  <xsl:param name="space" select="10" /><!-- space between bars and items -->

  <xsl:variable name="max-y" select="//detail[not(//detail &gt; .)][1]" />

  <xsl:template match="root">
    <svg>
      <defs>
        <style type="text/css"><![CDATA[
          g.bar text {
            font-family: Arial; 
            text-anchor: middle;
            fill: white;
          }
          g.bar rect {
            fill: black;
          }
        ]]></style>
      </defs>
      <g transform="translate(10, 10)">
        <xsl:apply-templates select="item" />
      </g>
    </svg>
  </xsl:template>

  <xsl:template match="item">
    <xsl:variable name="prev-item" select="preceding-sibling::item" />
    <g class="item" id="item-{position()}" transform="translate({
      count($prev-item/detail) * ($width + $space)
      + count($prev-item) * $space
    })">
      <xsl:apply-templates select="detail" />
    </g>
  </xsl:template>

  <xsl:template match="detail">
    <xsl:variable name="idx" select="count(preceding-sibling::detail)" />
    <xsl:variable name="pos" select="$idx * ($width + $space)" />
    <g class="bar">
      <rect x="{$pos}" y="{$max-y - .}" height="{.}" width="{$width}" />
      <text x="{$pos + $width div 2.0}" y="{$max-y - $space}">
        <xsl:value-of select="."/>
      </text>
    </g>
  </xsl:template>
</xsl:stylesheet>

生成

<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style type="text/css"><![CDATA[
              g.bar text {
                font-family: Arial; 
                text-anchor: middle;
                fill: white;
              }
              g.bar rect {
                fill: black;
              }
            ]]></style>
  </defs>
  <g transform="translate(10, 10)">
    <g class="item" id="item-1" transform="translate(0)">
      <g class="bar">
        <rect x="0" y="100" height="100" width="40"/>
        <text x="20" y="190">100</text>
      </g>
      <g class="bar">
        <rect x="50" y="0" height="200" width="40"/>
        <text x="70" y="190">200</text>
      </g>
    </g>
    <g class="item" id="item-2" transform="translate(110)">
      <g class="bar">
        <rect x="0" y="150" height="50" width="40"/>
        <text x="20" y="190">50</text>
      </g>
      <g class="bar">
        <rect x="50" y="100" height="100" width="40"/>
        <text x="70" y="190">100</text>
      </g>
    </g>
  </g>
</svg>

在我的电脑上,它的呈现效果如下:

SVG渲染结果


@antonpug:不用谢。如果有一些SVG代码可以作为起点就更好了,因为我很熟悉XSLT,但对SVG几乎一无所知。 - Tomalak
比我的建议好多了,赞。 - 3martini
@3martini 没有你的解决方案,这个不可能存在。 - Tomalak

7

SVG只是一种特殊的XML,可以参考http://www.w3.org/TR/SVG/intro.html

你可以从<svg>标签开始嵌套。

XSL是一个完全不同的世界。我使用了一个参考示例http://www.carto.net/svg/samples/xslt/#basic并修改了它以便与您的XML一起使用,以演示如何使用XSL处理嵌套数据。基本上,我只添加了一个内部的for-each循环。

这里是一个起点示例:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="root">
    <svg width="200px" height="500px" xmlns="http://www.w3.org/2000/svg">
      <g id="bar" transform="translate(0,200)">
        <xsl:for-each select="item">
          <xsl:variable name="item_position" select="(position()-1) * 100"/>
          <xsl:for-each select="detail">
            <xsl:variable name="val" select="."/>
            <rect x="{$item_position + position()*40}" y="-{$val}" height="{$val}" width="35" style="fill:{@fill};"/>
            <text x="{$item_position + position()*40 + 15}" y="-{($val div 2.0) - 5}" style="font-family:arial;text-anchor:middle;baseline-shift:-15;fill:white">
              <xsl:value-of select="."/>
            </text>
          </xsl:for-each>
        </xsl:for-each>
      </g>
    </svg>
  </xsl:template>
</xsl:transform>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接