2014-12-11 2 views
0

У меня есть следующий вопрос относительно преобразования XSLT. Мне нужно преобразовать XML-файл с учетом значений атрибутов во всех родительских узлах до корня. Так имея такой код (рассмотрим атрибут XY в виде строки со значениями «X, Y»):Преобразование XSLT в attibute

<Layout XY="40,20"> 
    <Layout XY="0,20"> 
    <Circle OffsetX="0"/> 
    </Layout> 
    <Circle OffsetX="6" /> 
    <Layout XY="100,20"> 
    <Circle OffsetX="0"/> 
    <Layout XY="200,20"> 
     <Circle OffsetX="5"/> 
    </Layout> 
    </Layout> 
</Layout> 

мне нужен выходной сигнал

<Layout XY="40,20"> 
    <Layout XY="0,20"> 
    <Circle OffsetX="40"/> 
    </Layout> 
    <Circle OffsetX="46" /> 
    <Layout XY="100,20"> 
    <Circle OffsetX="140"/> 
    <Layout XY="200,20"> 
     <Circle OffsetX="345"/> 
    </Layout> 
    </Layout> 
</Layout> 

Я пытаюсь использовать XSLT преобразования используя шаблон, подобный этому:

<!-- Copy template --> 

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

<!-- Change values --> 

<xsl:template match="Circle/@OffsetX"> 
    <xsl:param name="newOffsetX" select="substring-before(../../@XY,',')"/> 
    <xsl:apply-templates select="@*|node()"/>  
    <xsl:attribute name="OffsetX"> 
     <xsl:value-of select=".+$newOffsetX"/> 
    </xsl:attribute>    
</xsl:template> 

но это решение только на один уровень вверх. Возможно ли вообще сделать такое преобразование только с использованием XSLT?

+0

"* но это решение только для одного уровня вверх *" На самом деле, это не работает вообще. Родитель атрибута «OffsetX» является самим элементом Circle, поэтому выражение «../@ XY» ничего не выбирает, а результатом добавления является «NaN». –

+0

О, да, извините - это должно быть «substring-before (../../@ XY, ',')« Я опустил выбор бабушки и дедушки этого атрибута - не на prupose – zaxik

ответ

1

Если вы работаете с XSLT 1.0, вам нужен шаблон, который рекурсивно пересекает вверх по дереву узлов:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 

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

    <xsl:template match="@XY" mode="sumXOffset" name="SumXOffset"> 
    <xsl:param name="currentValue" 
       select="substring-before(., ',')" /> 

    <xsl:variable name="sum"> 
     <xsl:apply-templates select="(../ancestor::*/@XY)[last()]" mode="sumXOffset" /> 
    </xsl:variable> 

    <xsl:value-of select="$currentValue + 
          concat('0', $sum)" /> 
    </xsl:template> 

    <xsl:template match="@OffsetX"> 
    <xsl:attribute name="{name()}"> 
     <xsl:call-template name="SumXOffset"> 
     <xsl:with-param name="currentValue" select="." /> 
     </xsl:call-template> 
    </xsl:attribute> 
    </xsl:template> 
</xsl:stylesheet> 

При запуске на ваш образец XML, результат:

<Layout XY="40,20"> 
    <Layout XY="0,20"> 
    <Circle OffsetX="40" /> 
    </Layout> 
    <Circle OffsetX="46" /> 
    <Layout XY="100,20"> 
    <Circle OffsetX="140" /> 
    <Layout XY="200,20"> 
     <Circle OffsetX="345" /> 
    </Layout> 
    </Layout> 
</Layout> 

Если у вас есть процессор XSLT 2.0, решение намного проще:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 

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

    <xsl:template match="@OffsetX"> 
    <xsl:attribute name="{name()}"> 
     <xsl:value-of select=". + sum(ancestor::*/@XY/substring-before(','))"/> 
    </xsl:attribute> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Хороший рекурсивный код XSLT, +1. –

+0

спасибо! Это то, что я искал – zaxik

1

Я бы сделал это так:

XSLT 1,0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common" 
extension-element-prefixes="exsl"> 
<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="Circle/@OffsetX"> 
    <xsl:variable name="previous-X"> 
     <xsl:for-each select="ancestor::*[@XY]"> 
      <x><xsl:value-of select="substring-before(@XY, ',')"/></x> 
     </xsl:for-each> 
    </xsl:variable> 
    <xsl:attribute name="OffsetX"> 
     <xsl:value-of select=". + sum(exsl:node-set($previous-X)/x)"/> 
    </xsl:attribute>    
</xsl:template> 

</xsl:stylesheet> 
+0

Nice Exsl: набор узлов или метод переменной (xslt2), +1. –

0

Существует способ добиться этого в XSLT 1.0 без рекурсивного шаблона (не то, что нет ничего плохого в этом!), И это должно пройти нарастающим итогом с шаблоном к шаблону в качестве параметра. По существу, используйте параметр newOffsetX в шаблоне идентификации.

Попробуйте этот XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="@*|node()"> 
     <xsl:param name="newOffsetX" select="0" /> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"> 
       <xsl:with-param name="newOffsetX" select="$newOffsetX + number(substring-before(concat('0', @XY, ','), ','))" /> 
      </xsl:apply-templates> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@OffsetX"> 
     <xsl:param name="newOffsetX" select="0" /> 
     <xsl:attribute name="OffsetX"> 
      <xsl:value-of select="$newOffsetX + number(.)" /> 
     </xsl:attribute> 
    </xsl:template> 
</xsl:stylesheet> 

смешное выражение substring-before(concat('0', @XY, ','), ',') мог справиться с узлами без каких-либо @XY атрибута. Когда присутствует атрибут @XY со значением «40,20», например, «concat» возвращает «040,20», и поэтому число X все равно численно одинаково. Когда @XY нет, concat возвращает 0,, и поэтому число X равно «0».

Смежные вопросы