XSLT - 与身份模式一起更改节点上下文的问题

6
我有一个给定的源XML文档,其结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<sample>
  <definition>
    <variable>
      <name>object01_ID_138368350261919620</name>
      <value>NUL</value>
    </variable>  
    <variable>
      <name>param01_ID_138368350261919621</name>
      <value>10</value>
    </variable>
    <variable>
      <name>param02_ID_138368350261919622</name>
      <value>100</value>
    </variable>
  </definition>
  <override>
    <assignment>
      <name>object01_ID_138368350261919620</name>
      <path>module01/object01</path>
    </assignment>
    <assignment>
      <name>param01_ID_138368350261919621</name>
      <path>module01/object01/param01</path>
    </assignment>
    <assignment>
      <name>param02_ID_138368350261919622</name>
      <path>module01/object01/param02</path>
    </assignment>
  </override>
</sample>

源XML的特点是: <override>元素中的每个<assignment>元素都对应于<definition>元素中的一个<variable>元素。这种1:1的关系是通过它们的<name>元素的内容建立的。
转化要求和目标XML如下: 根据元素中元素的

当你提到“例如添加param03...”时-- param03 是如何添加的?我不清楚你在顶部提到的源XML应该如何转换为你发布的结果XML。 - ABach
通过_param03_的示例,我想展示两个XSLT在添加<override>元素中的<assignment>元素方面都非常有效(请参见注释“处理变量赋值”后的代码)。试一试。我描述的问题出现在转换所需的添加相应的<variable>元素到<definition>元素中的部分(请参见注释“处理变量定义”后的代码)。对于这个转换的部分,我尝试了两种方法(_for-each_和_apply-templates_),但不幸失败了。 - Stephan Rudolph
我猜用户不愿回答这个问题,因为它过于冗长和啰嗦。请用非常简洁的几句话说明您希望输入的XML如何更改。(然而,很明显您已经努力自己解决了这个问题)。 - Mathias Müller
非常感谢您的提示。我已经将其缩短了。现在希望它更容易阅读。我仍在寻找解决方案。任何帮助都将不胜感激。 - Stephan Rudolph
1个回答

1
你遇到的问题是 xsl:for-each 没有改变输出上下文,它只改变了迭代上下文。
当你迭代 xsl:for-each select="/sample/definition/*[position()=last()]" 时,你仍然在 assignment(模板匹配)的上下文中输出。
你需要从 definition 的上下文中输出新的 variable
我修改了你的 XSLT 以产生你想要的结果。这可能不是最终解决方案,但应该让你更接近目标。我添加了注释(全大写)来尝试解释我所做的更改。如果有问题,请告诉我。
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:fn="http://www.w3.org/2005/xpath-functions" 
    exclude-result-prefixes="fo xs fn">

    <!--
  global declarations ==========================================================
  -->
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- randomid here is just a fake for sake of simplification -->
    <xsl:param name="randomid" select="138368350261919623"/>
    <!--MOVED PARAMS FROM ORIGINAL TEMPLATE HERE SO THEY CAN BE USED BY MULTIPLE TEMPLATES -->
    <xsl:param name="value_node_name_target">
        <xsl:value-of select="concat('Param03_ID', '_', $randomid)"/>
    </xsl:param>
    <xsl:param name="value_node_path_target">
        <!--I LEFT THE PREDICATE BECAUSE IT APPEARS THAT THERE COULD BE MORE THAN ONE override ELEMENT.-->
        <xsl:value-of select="concat(/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
            /assignment[1]/path, '/param03')"/>
    </xsl:param>
    <xsl:param name="value_node_value_target" select="'1000'"/>

    <!--
  template - identity ==========================================================
  -->
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>

    <!--
  template - variable assignment ===============================================
  -->
    <!--MOVED CODE FROM THIS TEMPLATE INTO THE assignment TEMPLATE-->

    <!--REPLACES THE xsl:for-each THAT PROCESSES THE VARIABLE DEFINITION-->
    <xsl:template match="definition[../override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
        /assignment[path[matches(text(), '.*/object01$')]]]/*[last()]">
        <xsl:message select="'processing: variable definition'"/>
        <xsl:message select="concat('Here we are: ', .)"/>
        <xsl:message select="concat('applying name: ', $value_node_name_target)"/>
        <xsl:message select="concat('applying value: ', $value_node_value_target)"/>
        <xsl:call-template name="identity"/>
        <variable>
            <name>
                <xsl:value-of select="$value_node_name_target"/>
            </name>
            <value>
                <xsl:value-of select="$value_node_value_target"/>
            </value>
        </variable>
    </xsl:template>

    <!--
    template - processing param03 =============================================
  -->
    <xsl:template match="/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
        /assignment[path[matches(text(), '.*/object01$')]]">
        <!-- setting params -->
        <!--MOVED TEMPLATE PARAMS TO GLOBAL PARAMS-->
        <!-- processing variable assignment -->
        <!--REPLACED UNNECESSARY xsl:call-template WITH ACTUAL CODE-->
        <xsl:message select="'processing: variable assignment'"/>
        <xsl:message select="concat('applying name: ', $value_node_name_target)"/>
        <xsl:message select="concat('applying path: ', $value_node_path_target)"/>
        <xsl:call-template name="identity"/>
        <assignment>
            <name>
                <xsl:value-of select="$value_node_name_target"/>
            </name>
            <!--CHANGED FROM xpath TO path (APPEARED TO BE A TYPO)-->
            <path>
                <xsl:value-of select="$value_node_path_target"/>
            </path>
        </assignment>       <!-- processing variable definition -->
        <!--THIS IS NOW DONE BY A SEPARATE MATCHING TEMPLATE-->
    </xsl:template>
</xsl:stylesheet>

输出

<sample>
   <definition>
      <variable>
         <name>object01_ID_138368350261919620</name>
         <value>NUL</value>
      </variable>
      <variable>
         <name>param01_ID_138368350261919621</name>
         <value>10</value>
      </variable>
      <variable>
         <name>param02_ID_138368350261919622</name>
         <value>100</value>
      </variable>
      <variable>
         <name>Param03_ID_138368350261919623</name>
         <value>1000</value>
      </variable>
   </definition>
   <override>
      <assignment>
         <name>object01_ID_138368350261919620</name>
         <path>module01/object01</path>
      </assignment>
      <assignment>
         <name>param01_ID_138368350261919621</name>
         <path>module01/object01/param01</path>
      </assignment>
      <assignment>
         <name>param02_ID_138368350261919622</name>
         <path>module01/object01/param02</path>
      </assignment>
      <assignment>
         <name>Param03_ID_138368350261919623</name>
         <path>module01/object01/param03</path>
      </assignment>
   </override>
</sample>

非常感谢您的快速回复。您的解决方案有效,但还没有解决我的问题。挑战在于,任务必须始终是主要任务。这意味着,如果样本param03的匹配规则评估为true(也可能为false),则根据个人ID(递增基数)进行处理,如“转换和目标XML的要求”所示。因此,在我看来,创建定义必须通过创建分配(例如param03)进行参数化。但然后我的问题又回来了。 - Stephan Rudolph
任何需要为“assignment”进行的测试也可以为“definition”进行。我已经向“definition”模板添加了一个谓词。我只修改了您的XSLT版本,因此我没有尝试添加任何ID的递增。如果需要,我可以在有更多时间时再尝试查看。 - Daniel Haley
非常感谢您的提议。我只是提到ID这个事实,以强调任务和定义之间存在强烈的依赖关系。您提出的对定义模板添加谓词可能是一种解决方法。但对我来说,如何在运行时同时提供两者的相同ID还不清楚。请参见上文(您提出的XSLT 2.0,由我进行了修改),我是如何修改您提出的XSLT的。不幸的是,它仍然产生相同错误的XML。 - Stephan Rudolph

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