Как превратить XML-файл в SVG с помощью XSL?

дано

<root>
   <item>
      <detail>100</detail>
      <detail>200</detail>
   </item>
   <item>
      <detail>50</detail>
      <detail>100</detail>
   </item>
</root>

Как я мог бы сделать эти данные в простую гистограмму SVG? (ничего необычного, просто четыре бара, представляющие отношение между числами в некотором роде)

что-то вроде этого: enter image description here (Я знаю, что нет разделения между двумя элементами, но давайте просто скажем, что я сделаю их разными цветами, первые два бара синими, а второй красный)

Я думаю, я не уверен, что синтаксис внутри шаблона xsl:будет генерировать SVG код? Лучший ответ принимается!

2 ответов


вот пример с еще несколькими колокольчиками и свистками:

  • она автоматически масштабируется до максимальной высоты
  • он использует CSS для элементов стиля
  • он имеет настраиваемые параметры для ширины и расстояния между барами
  • он использует более идиоматический 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 rendering result

на моей машине.


SVG - это просто особый вид xml, проверьте ссылку наhttp://www.w3.org/TR/SVG/intro.html

вы начинаете с <svg> tag и начать вложенность.

xsl-это совершенно другой мир. Я использовал ссылочный пример вhttp://www.carto.net/svg/samples/xslt/#basic и изменил его для работы с xml, чтобы продемонстрировать, как использовать xsl для вложенных данных. В принципе, я просто добавил внутренний цикл для каждого цикла.

здесь пример начальной точки:

<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>