XSLT - 如何在使用流从变量节点进行内部循环时复制父节点?

XSLT - How can I copy parent node when inside loop from a variable node using streaming?

在迭代循环中使用模板匹配进行流式传输时,我正在努力复制我的父节点数据,该路径来自另一个 xml 我通过地图访问。

我得到的是:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
        <Status>Error: Invalid ID value. '' is not a valid ID value for type =
            'Custom_Worktag_13_ID'. Error: Invalid ID value. '' is not a valid ID value for type =
            'Custom_Organization_Reference_ID'. Error: Invalid ID value. '133746-GP OM Internal
            Labor-1' is not a valid ID value for type = 'Project_Plan_ID'. Error: Invalid ID value.
            '12345' is not a valid ID value for type = 'Employee_ID'.</Status>
    </row>
</root>

但我正在寻找的输出是:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
        <Status>Error: Invalid ID value. '' is not a valid ID value for type =
            'Custom_Worktag_13_ID'.</Status>
    </row>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
        <Status>Error: Invalid ID value. '' is not a valid ID value for type =
            'Custom_Organization_Reference_ID'.</Status>
    </row>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
        <Status>Error: Invalid ID value. '133746-GP OM Internal Labor-1' is not a valid ID value for
            type = 'Project_Plan_ID'.</Status>
    </row>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
        <Status>Error: Invalid ID value. '12345' is not a valid ID value for type =
            'Employee_ID'.</Status>
    </row>
</root>

XSLT 输入XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <row>
        <Record>1</Record>
        <Employee-ID>12345</Employee-ID>
        <Authorization-ID>133746</Authorization-ID>
        <Date>2021-06-22</Date>
        <Quantity>2</Quantity>
        <Task-ID>PRJTASK0011134</Task-ID>
        <Project-Plan-ID>133746-GP OM Internal Labor-1</Project-Plan-ID>
        <Comments/>
    </row>
</root>

错误变量数据:

<?xml version="1.0" encoding="UTF-8"?>
<errors>
    <error>
        <lineNumber>1</lineNumber>
        <errorGroup>
            <errorRow>
                <severity>Error</severity>
                <message>Invalid ID value. '' is not a valid ID value for type =
                    'Custom_Worktag_13_ID'</message>
            </errorRow>
            <errorRow>
                <severity>Error</severity>
                <message>Invalid ID value. '' is not a valid ID value for type =
                    'Custom_Organization_Reference_ID'</message>
            </errorRow>
            <errorRow>
                <severity>Error</severity>
                <message>Invalid ID value. '133746-GP OM Internal Labor-1' is not a valid ID value
                    for type = 'Project_Plan_ID'</message>
            </errorRow>
            <errorRow>
                <severity>Error</severity>
                <message>Invalid ID value. '12345' is not a valid ID value for type =
                    'Employee_ID'</message>
            </errorRow>
        </errorGroup>
    </error>
</errors>

当前的 XSLT 3 代码:

<?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:map="http://www.w3.org/2005/xpath-functions/map" version="3.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:mode name="streaming" streamable="yes" on-no-match="shallow-skip"/>
    <xsl:mode name="in-memory" streamable="no"/>
    <xsl:variable name="lineKey" as="map(xs:string, element())">
        <xsl:map>
            <xsl:call-template name="generateErrorFileMap"/>
        </xsl:map>
    </xsl:variable>
    <xsl:template match="root">
        <root>
            <xsl:apply-templates select="row/copy-of()" mode="in-memory"/>
        </root>
    </xsl:template>
    <xsl:template match="row" mode="in-memory">
        <xsl:choose>
            <xsl:when test="map:contains($lineKey, Record)">
                <xsl:iterate select="map:get($lineKey, Record)/errorGroup/errorRow">
                    <row>
                        <!-- Copy Nodes -->
                        <xsl:apply-templates/>
                        <Status>
                            <xsl:value-of select="concat(severity, ': ', message, '.')"/>
                        </Status>
                    </row>
                </xsl:iterate>
            </xsl:when>
            <xsl:otherwise>
                <row>
                    <!-- Copy Nodes -->
                    <xsl:apply-templates/>
                    <status>
                        <xsl:value-of select="'Successfully loaded.'"/>
                    </status>
                </row>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <!-- standard copy template -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template name="generateErrorFileMap">
        <xsl:source-document href="mctx:vars/errorFile" streamable="yes">
            <xsl:for-each select="/errors/error/copy-of()">
                <xsl:map-entry key="lineNumber =&gt; string()">
                    <map>
                        <xsl:apply-templates select="errorGroup"/>
                    </map>
                </xsl:map-entry>
            </xsl:for-each>
        </xsl:source-document>
    </xsl:template>
</xsl:stylesheet>

以优化方式实现我想要的输出的最佳方法是什么?

我认为

的结果
<xsl:apply-templates select="errorGroup"/>

将是一个 errorGroup 元素,这意味着 map:get($lineKey, Record) 的结果将是一个 errorGroup 元素,这意味着 map:get($lineKey, Record)/errorGroup 将 select 没有(因为 errorGroup 元素没有 errorGroup 子元素)。尝试将映射的类型声明更改为as="map(xs:string, element(errorGroup))"以加强类型检查。

这是我在快速代码检查中唯一注意到的事情,但您没有告诉我们它是如何失败的;很难诊断一个看不见的问题。

我认为这是 xsl:merge 的任务(至少如果行和错误按 lineNumber 和 Record 整数值排序):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="xml" indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template name="xsl:initial-template">
    <root>
      <xsl:merge>
        <xsl:merge-source name="record" for-each-source="'record-list.xml'" streamable="yes" select="root/row">
          <xsl:merge-key select="xs:integer(Record)"/>
        </xsl:merge-source>
        <xsl:merge-source name="error" for-each-source="'error-list.xml'" streamable="yes" select="errors/error">
          <xsl:merge-key select="xs:integer(lineNumber)"/>
        </xsl:merge-source>
        <xsl:merge-action>
          <xsl:choose>
            <xsl:when test="not(current-merge-group('error'))">
              <xsl:apply-templates select="current-merge-group('record')">
                <xsl:with-param name="status" select="'Successfully loaded.'"/>
              </xsl:apply-templates>
            </xsl:when>
            <xsl:when test="current-merge-group('record') and current-merge-group('error')">
              <xsl:for-each select="current-merge-group('error')/errorGroup/errorRow">
                <xsl:apply-templates select="current-merge-group('record')">
                  <xsl:with-param name="status" select="severity || ':' || message"/>
                </xsl:apply-templates>
              </xsl:for-each>
            </xsl:when>
          </xsl:choose>
        </xsl:merge-action>
      </xsl:merge>
    </root>
    <xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
  </xsl:template>
  
  <xsl:template match="row/*[last()]">
    <xsl:param name="status"/>
    <xsl:next-match/>
    <status>{$status}</status>
  </xsl:template>

</xsl:stylesheet>

运行 Saxon EE 使用 -it 选项而不是 -s 源选项。

修复原代码,我觉得不是

      <xsl:when test="map:contains($lineKey, Record)">
            <xsl:iterate select="map:get($lineKey, Record)/errorGroup/errorRow">
                <row>
                    <!-- Copy Nodes -->
                    <xsl:apply-templates/>
                    <Status>
                        <xsl:value-of select="concat(severity, ': ', message, '.')"/>
                    </Status>
                </row>
            </xsl:iterate>
        </xsl:when>

你想要例如

      <xsl:variable name="record" select="."/>
      <xsl:when test="map:contains($lineKey, Record)">
            <xsl:iterate select="map:get($lineKey, Record)/errorGroup/errorRow">
              <xsl:apply-templates select="$record">
                <xsl:with-param name="status" select="severity || ': ' || message || '.'"/>
              </xsl:apply-templates>
            </xsl:iterate>
        </xsl:when>

加一个模板(范围内需要 expand-text="yes"

  <xsl:template match="row/*[last()]">
    <xsl:param name="status"/>
    <xsl:next-match/>
    <status>{$status}</status>
  </xsl:template>