【问题标题】:XSLT integer evaluator, how to implement n-ary sum and multiplication?XSLT 整数求值器,如何实现 n 元求和和乘法?
【发布时间】:2015-03-31 22:06:36
【问题描述】:

我正在尝试实现一个小的整数求值器。事实上,它处理的 xml 文档有一个表达式,以及一个 varDef 列表以及可能变量的值。

XSLT 将该 XML 文档转换为另一个带有结果的文档。

这是 XML 文档的 XML 模式:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    xmlns:ej3="http://procesadores.ejemplo.com/Ej3"
    targetNamespace="http://procesadores.ejemplo.com/Ej3" 
    elementFormDefault="qualified"> 

    <element name="documento">
        <complexType>
            <sequence>
                <element ref="ej3:expr"/>
                <element ref="ej3:varDef" maxOccurs="unbounded" minOccurs="0"/>
            </sequence>
        </complexType>
    </element>

    <element name="expr" abstract="true"/>

    <element name="suma" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="resta" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="mult" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="div" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="mod" type="ej3:expBinaria" substitutionGroup="ej3:expr"/>
    <element name="opuesto" type="ej3:expUnaria" substitutionGroup="ej3:expr"/>
    <element name="abs" type="ej3:expUnaria" substitutionGroup="ej3:expr"/>

    <element name="var" type="ej3:tipoNombreVar" substitutionGroup="ej3:expr"/>
    <element name="cons" type="integer" substitutionGroup="ej3:expr"/>

    <element name="varDef">
        <complexType>
            <simpleContent>
                <extension base="int">
                    <attribute name="nombre" type="ej3:tipoNombreVar"/>
                </extension>
            </simpleContent>
        </complexType>
    </element>

    <complexType name="expUnaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="1" maxOccurs="1"/>
        </sequence>
    </complexType>

    <complexType name="expBinaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="2" maxOccurs="2"/>
        </sequence>
    </complexType>

    <complexType name="expNaria">
        <sequence>
            <element ref="ej3:expr" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
    </complexType>


    <simpleType name="tipoNombreVar">
        <restriction base="string">
            <pattern value="[a-zA-Z][a-zA-Z0-9]*"/>
        </restriction>
    </simpleType>
</schema>

这是 XSLT 文档:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ej3="http://procesadores.ejemplo.com/Ej3"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:strip-space elements="ej3:*"/>

    <xsl:output
        method="xml"
        indent="yes"
        encoding="utf-8"/>

    <xsl:key name="defVariables" match="ej3:varDef" use="@nombre"/>

    <xsl:template match="/ej3:documento"> 
        <cons><xsl:apply-templates select="*[not(local-name()='varDef')]"/></cons>
    </xsl:template>

    <xsl:template match="ej3:suma">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 + $s2"/>
    </xsl:template>

    <xsl:template match="ej3:resta">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 - $s2"/>
    </xsl:template>

    <xsl:template match="ej3:mult">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 * $s2"/>
    </xsl:template>

    <xsl:template match="ej3:div">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="$s2 = 0">
                <xsl:value-of select="$s1 div $s2"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="xs:integer($s1 div $s2)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="ej3:mod">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:variable name="s2">  
            <xsl:apply-templates select="child::node()[2]"/>
        </xsl:variable>
        <xsl:value-of select="$s1 mod $s2"/>
    </xsl:template>

    <xsl:template match="ej3:opuesto">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:value-of select="- $s1"/>
    </xsl:template>

    <xsl:template match="ej3:abs">
        <xsl:variable name="s1">  
            <xsl:apply-templates select="child::node()[1]"/>
        </xsl:variable>
        <xsl:value-of select="abs($s1)"/>
    </xsl:template>

    <xsl:template match="ej3:var">
        <xsl:value-of select="key('defVariables',.)"/>
    </xsl:template>    

    <xsl:template match="ej3:cons">
        <test><xsl:value-of select="."/></test>
    </xsl:template>

</xsl:stylesheet>

这一切都按我的预期工作。但我想制作 suma(sum) 和 mult n-ary 运算符。也就是说,像这样:

<suma>
    <cons>1</cons>
    <cons>2</cons>
    <cons>3</cons>
</suma>

应该能够被评估。为了使它工作,我必须修改suma xsl:template,但我不太确定如何去做。我已经尝试了几件事,但事实上我必须在将孩子相加之前以某种方式对其进行评估,这让我很难找到解决方案。

您能建议如何实现这一目标吗?

请注意,我希望summult 操作数都以这种方式工作,因此基于xpath 函数sum() 的解决方案不适用于mult

【问题讨论】:

    标签: xslt integer xslt-2.0 evaluation


    【解决方案1】:

    我会确保您的模板返回xs:integer 的序列,例如改变

    <xsl:template match="ej3:cons">
        <test><xsl:value-of select="."/></test>
    </xsl:template>
    

    <xsl:template match="ej3:cons">
        <xsl:sequence select="xs:integer(.)"/>
    </xsl:template>
    

    那么你可以使用

    <xsl:template match="suma">
      <xsl:variable name="operands" as="xs:integer+">
        <xsl:apply-templates select="*"/>
      </xsl:variable>
      <xsl:sequence select="sum($operands)"/>
    </xsl:template>
    

    对于乘法,您可以使用一个函数(需要声明一个前缀 mf 绑定到某个命名空间)

    <xsl:function name="mf:multiply" as="xs:integer">
      <xsl:param name="operands" as="xs:integer+"/>
      <xsl:sequence select="if (not(exists($operands[2])))
                            then $operands[1]
                            else $operands[1] * mf:multiply($operands[position() gt 1])"/>
    </xsl:function>
    

    然后在

    中使用它
    <xsl:template match="multa">
      <xsl:variable name="operands" as="xs:integer+">
        <xsl:apply-templates select="*"/>
      </xsl:variable>
      <xsl:sequence select="mf:multiply($operands)"/>
    </xsl:template>
    

    这是一个例子:

    <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:mf="http://example.com/mf"
      exclude-result-prefixes="xs mf">
    
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:function name="mf:multiply" as="xs:integer">
      <xsl:param name="operands" as="xs:integer+"/>
      <xsl:sequence select="if (not(exists($operands[2])))
                            then $operands[1]
                            else $operands[1] * mf:multiply($operands[position() gt 1])"/>
    </xsl:function>
    
    <xsl:template match="expression">
      <result>
        <xsl:apply-templates/>
      </result>
    </xsl:template>
    
    <xsl:template match="cons">
        <xsl:sequence select="xs:integer(.)"/>
    </xsl:template>
    
    <xsl:template match="suma">
      <xsl:variable name="operands" as="xs:integer+">
        <xsl:apply-templates select="*"/>
      </xsl:variable>
      <xsl:sequence select="sum($operands)"/>
    </xsl:template>
    
    <xsl:template match="multa">
      <xsl:variable name="operands" as="xs:integer+">
        <xsl:apply-templates select="*"/>
      </xsl:variable>
      <xsl:sequence select="mf:multiply($operands)"/>
    </xsl:template>
    
    </xsl:stylesheet>
    

    示例输入为

    <expression>
      <suma>
        <cons>1</cons>
        <cons>2</cons>
        <cons>3</cons>
        <multa>
          <cons>1</cons>
          <cons>2</cons>
          <cons>3</cons>
        </multa>
      </suma>
    </expression>
    

    我得到了输出&lt;result&gt;12&lt;/result&gt;

    【讨论】:

    • 我如何使用这种方法来实现 n 元乘法?
    • @Trollkemada,我已经用一些建议编辑了答案,应该有助于实现它,尽管未经测试并且只能直接在 SO 编辑器中输入。
    • mult 和 sum 都将结果连接为字符串,这是我正在尝试的所有问题。我在一些在线 xslt 测试仪中进行了测试,结果相同。
    • @Trollkemada,我进行了一些编辑并添加了一个简短但完整的示例,应该可以完成这项工作。如果您仍有问题,请编辑您的问题并提供一些示例表达式的 XML 输入文档。
    • 并注意使用xsl:sequence 而不是xsl:value-of 是必不可少的。
    猜你喜欢
    • 1970-01-01
    • 2016-10-23
    • 2021-11-13
    • 2018-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    相关资源
    最近更新 更多