Apply-Template 仅适用于 ID 基于父子节点列表的节点

Posted

技术标签:

【中文标题】Apply-Template 仅适用于 ID 基于父子节点列表的节点【英文标题】:Apply-Template only for nodes with ID based on parent subnode list 【发布时间】:2021-05-27 14:29:51 【问题描述】:

我对 XSLT 还很陌生,并试图解决一个复杂的 XML 问题。 这是关于 XML 和所需输出的示例。

问题是我需要根据OrderIdentityOrder 选择Identity。比较应该在IdentityId 上完成。

如果身份的LastName 为空,则该订单不应包含在列表中

正如您从我的实验中看到的那样,我可以通过转换来完成这项工作,但我无法弄清楚如何排除 Identity 的 LastName 为空的订单。

感谢我能得到的所有帮助;)

<Orders>
    <Order>
        <OrderNo>OR1</OrderNo>
        <BookingHeader>
            <BookingHeaderID>1</BookingHeaderID>
            <Identities>
                <Identity>
                    <IdentityId>1</IdentityId>
                    <FirstName>John</FirstName>
                    <LastName>Doe</LastName>
                </Identity>
                <Identity>
                    <IdentityId>2</IdentityId>
                    <FirstName>Petter</FirstName>
                    <LastName>Smart</LastName>
                </Identity>
                <Identity>
                    <IdentityId>3</IdentityId>
                    <FirstName>Betty</FirstName>
                    <LastName>Blue</LastName>
                </Identity>
            </Identities>
        </BookingHeader>
        <IdentityOrders>
            <IdentityOrder>
                <IdentityId>1</IdentityId>
                <Name>John Doe</Name>
            </IdentityOrder>
        </IdentityOrders>
    </Order>
    <Order>
        <OrderNo>OR2</OrderNo>
        <BookingHeader>
            <BookingHeaderID>1</BookingHeaderID>
            <Identities>
                <Identity>
                    <IdentityId>1</IdentityId>
                    <FirstName>John</FirstName>
                    <LastName>Doe</LastName>
                </Identity>
                <Identity>
                    <IdentityId>2</IdentityId>
                    <FirstName>Petter</FirstName>
                    <LastName>Smart</LastName>
                </Identity>
                <Identity>
                    <IdentityId>3</IdentityId>
                    <FirstName>Betty</FirstName>
                    <LastName>Blue</LastName>
                </Identity>
            </Identities>
        </BookingHeader>
        <IdentityOrders>
            <IdentityOrder>
                <IdentityId>1</IdentityId>
                <Name>John Doe</Name>
            </IdentityOrder>
            <IdentityOrder>
                <IdentityId>3</IdentityId>
                <Name>Betty Blue</Name>
            </IdentityOrder>
        </IdentityOrders>
    </Order>
    <Order>
        <OrderNo>OR3</OrderNo>
        <BookingHeader>
            <BookingHeaderID>1</BookingHeaderID>
            <Identities>
                <Identity>
                    <IdentityId>1</IdentityId>
                    <FirstName>John</FirstName>
                    <LastName>Doe</LastName>
                </Identity>
                <Identity>
                    <IdentityId>2</IdentityId>
                    <FirstName>Petter</FirstName>
                    <LastName></LastName>
                </Identity>
                <Identity>
                    <IdentityId>3</IdentityId>
                    <FirstName>Betty</FirstName>
                    <LastName>Blue</LastName>
                </Identity>
            </Identities>
        </BookingHeader>
        <IdentityOrders>
            <IdentityOrder>
                <IdentityId>2</IdentityId>
                <Name>Petter</Name>
            </IdentityOrder>
            <IdentityOrder>
                <IdentityId>3</IdentityId>
                <Name>Betty Blue</Name>
            </IdentityOrder>
        </IdentityOrders>
    </Order>
    <Order>
        <OrderNo>OR4</OrderNo>
        <BookingHeader>
            <BookingHeaderID>2</BookingHeaderID>
            <Identities>
                <Identity>
                    <IdentityId>4</IdentityId>
                    <FirstName>Roger</FirstName>
                    <LastName>Moore</LastName>
                </Identity>
                <Identity>
                    <IdentityId>5</IdentityId>
                    <FirstName>Sylvester</FirstName>
                    <LastName></LastName>
                </Identity>
                <Identity>
                    <IdentityId>6</IdentityId>
                    <FirstName>Arnold</FirstName>
                    <LastName></LastName>
                </Identity>
            </Identities>
        </BookingHeader>
        <IdentityOrders>
            <IdentityOrder>
                <IdentityId>4</IdentityId>
                <Name>Roger Moore</Name>
            </IdentityOrder>
            <IdentityOrder>
                <IdentityId>5</IdentityId>
                <Name>Sylvester</Name>
            </IdentityOrder>
        </IdentityOrders>
    </Order>
</Orders>

改造后想要的结果

<?xml version="1.0" encoding="utf-8"?>
<Orders>
  <Order>
    <OrderNo>OR1</OrderNo>
    <Identities>
      <Identity>
        <IdentityId>1</IdentityId>
        <FirstName>John</FirstName>
        <LastName>Doe</LastName>
      </Identity>
    </Identities>
  </Order>
  <Order>
    <OrderNo>OR2</OrderNo>
    <Identities>
      <Identity>
        <IdentityId>1</IdentityId>
        <FirstName>John</FirstName>
        <LastName>Doe</LastName>
      </Identity>
      <Identity>
        <IdentityId>3</IdentityId>
        <FirstName>Betty</FirstName>
        <LastName>Blue</LastName>
      </Identity>
    </Identities>
  </Order>
  <Order>
    <OrderNo>OR3</OrderNo>
    <Identities>
      <Identity>
        <IdentityId>3</IdentityId>
        <FirstName>Betty</FirstName>
        <LastName>Blue</LastName>
      </Identity>
    </Identities>
  </Order>
  <Order>
    <OrderNo>OR4</OrderNo>
    <Identities>
      <Identity>
        <IdentityId>4</IdentityId>
        <FirstName>Roger</FirstName>
        <LastName>Moore</LastName>
      </Identity>
    </Identities>
  </Order>
</Orders>

XSLT 转换脚本。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="/*">
        <Orders>
            <xsl:apply-templates select="Order[BookingHeader/Identities/Identity[LastName[text()]]]" />
        </Orders>
    </xsl:template>
    <xsl:template match="Order">tra
        <Order>
            <OrderNo>
                <xsl:value-of select="OrderNo" />
            </OrderNo>
            <Identities>
                <xsl:for-each select="IdentityOrders/IdentityOrder">
                    <xsl:variable name="id" select="IdentityId" />
                    <xsl:for-each select="../../BookingHeader/Identities/Identity">
                        <xsl:if test="(IdentityId=$id) and (LastName!='') ">
                            <xsl:apply-templates select="." />
                        </xsl:if>
                    </xsl:for-each>
                </xsl:for-each>
            </Identities>
        </Order>
    </xsl:template>

    <xsl:template match="Identity">
        <Identity>
            <IdentityId>
                <xsl:value-of select="IdentityId" />
            </IdentityId>
            <FirstName>
                <xsl:value-of select="FirstName" />
            </FirstName>
            <LastName>
                <xsl:value-of select="LastName" />
            </LastName>
        </Identity>
    </xsl:template>
</xsl:stylesheet>

【问题讨论】:

您的示例非常令人困惑,因为 (a) 没有带有空 LastNama 的身份,并且 (b) 两个订单具有完全相同的身份,这使得很难看出哪个订单去了哪里。 【参考方案1】:

我猜(!)你想做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="id" match="Identity" use="concat(IdentityId, '|', ancestor::Order/OrderNo)" />

<xsl:template match="/Orders">
    <xsl:copy>
        <xsl:for-each select="Order">
            <xsl:variable name="id" select="key('id', concat(IdentityOrders/IdentityOrder/IdentityId, '|', OrderNo))" />
            <xsl:if test="$id/LastName/text()">
                <xsl:copy>
                    <xsl:copy-of select="OrderNo"/>
                    <Identities>
                        <xsl:copy-of select="$id"/>
                    </Identities>
                </xsl:copy>
            </xsl:if>
        </xsl:for-each> 
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

这使用 key 来选择Identity,其IdentityIdIdentityOrders 中列出。然后它检查选定的Identity 在其LastName 中是否有值,如果有,它会复制Order 及其OrderNo 和选定的Identity

请注意,您的 XML 结构允许 Order 拥有多个 IdentityOrder - 在这种情况下结果应该是什么尚不清楚。

【讨论】:

感谢您的回答。抱歉这个有点令人困惑的例子。是的,identityorders 中会有不止一个 identityorder。当我在 xml 中添加多个 identityorder 时,这种方法不起作用。你也有解决方案吗? 不,因为正如我所说,尚不清楚在这种情况下结果应该是什么。请编辑您的问题并阐明此处需要应用的逻辑。 所以我更新了我最初的例子。添加了更多订单和更多身份。 对不起,我不知道你想在这里完成什么。您是在举例说明,而不是在解释规则。我看到的唯一规则是“如果身份的姓氏为空,则订单不应包含在列表中” - 显然您的输出不遵循此规则,因为所有订单都包含在输出中。

以上是关于Apply-Template 仅适用于 ID 基于父子节点列表的节点的主要内容,如果未能解决你的问题,请参考以下文章

jQuery TableSorter 仅适用于 ID(数字字段),不适用于文本、日期等其他字段

Facebook Messenger ID匹配API仅适用于管理员

来自 JBoss - RedHat codeready 的基于 Eclipse 的图形 Camel 编辑器是不是仅适用于 xml 中的骆驼?

角度数据表排序仅适用于1列?

Parse Javascript Cloud Code .save() 仅适用于单个用户

GroupBy 转换运算符仅适用于本地环境