XSLT:通过比较子节点来转换节点

时间:2016-09-06 08:42:15

标签: xml python-3.x xslt

首先,我知道这个SO question有点不同。

我有一个XML文件,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
    <CstmrCdtTrfInitn>
        <GrpHdr>
            <MsgId>123</MsgId>
            <CreDtTm>321</CreDtTm>
            <NbOfTxs>10</NbOfTxs>
            <CtrlSum>18700.68</CtrlSum>
            <InitgPty>
                <Nm>some info</Nm>
            </InitgPty>
        </GrpHdr>

        <PmtInf>
            <!-- start -->
            <PmtInfId>asd</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>false</BtchBookg>
            <PmtTpInf>
                <InstrPrty>NORM</InstrPrty>
                <SvcLvl>
                    <Prtry>test</Prtry>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>date</ReqdExctnDt>
            <Dbtr>
                <Nm>something</Nm>
                <PstlAdr>
                    <AdrLine>addr 1</AdrLine>
                </PstlAdr>
            </Dbtr>
            <!-- end -->

            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>16082672122</InstrId>
                    <EndToEndId>16082672122</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="RON">2159.41</InstdAmt>
                </Amt>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>some bic</BIC>
                    </FinInstnId>
                </CdtrAgt>
            </CdtTrfTxInf>
        </PmtInf>

        <PmtInf>
            <!-- start -->
            <PmtInfId>asd</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>false</BtchBookg>
            <PmtTpInf>
                <InstrPrty>NORM</InstrPrty>
                <SvcLvl>
                    <Prtry>test</Prtry>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>date</ReqdExctnDt>
            <Dbtr>
                <Nm>something</Nm>
                <PstlAdr>
                    <AdrLine>addr 1</AdrLine>
                </PstlAdr>
            </Dbtr>
            <!-- end -->

            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>16082672122</InstrId>
                    <EndToEndId>16082672122</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="RON">2159.41</InstdAmt>
                </Amt>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>some bic</BIC>
                    </FinInstnId>
                </CdtrAgt>
            </CdtTrfTxInf>
        </PmtInf>

        <PmtInf>
            <!-- start -->
            <PmtInfId>asd</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>false</BtchBookg>
            <PmtTpInf>
                <InstrPrty>NORM</InstrPrty>
                <SvcLvl>
                    <Prtry>test</Prtry>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>date</ReqdExctnDt>
            <Dbtr>
                <Nm>something</Nm>
                <PstlAdr>
                    <AdrLine>addr 1</AdrLine>
                </PstlAdr>
            </Dbtr>
            <!-- end -->

            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>16082672122</InstrId>
                    <EndToEndId>16082672122</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="RON">2159.41</InstdAmt>
                </Amt>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>some bic</BIC>
                    </FinInstnId>
                </CdtrAgt>
            </CdtTrfTxInf>
        </PmtInf>

        <PmtInf>
            <!-- start -->
            <PmtInfId>asd</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>false</BtchBookg>
            <PmtTpInf>
                <InstrPrty>NORM</InstrPrty>
                <SvcLvl>
                    <Prtry>test</Prtry>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>date</ReqdExctnDt>
            <Dbtr>
                <Nm>something</Nm>
                <PstlAdr>
                    <AdrLine>addr 1</AdrLine>
                </PstlAdr>
            </Dbtr>
            <!-- end -->

            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>16082672122</InstrId>
                    <EndToEndId>16082672122</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="RON">2159.41</InstdAmt>
                </Amt>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>some bic</BIC>
                    </FinInstnId>
                </CdtrAgt>
            </CdtTrfTxInf>
        </PmtInf>
    </CstmrCdtTrfInitn>
</Document>
  • 如您所见,我有多个(4)<PmtInf></PmtInf>部分几乎相同的结构。
  • 我想做的是:

    1. 将第一个<PmtInfId>asd</PmtInfId>的{​​{1}}与第二个PmtInf的{​​{1}}进行比较。如果完全匹配(如同相同的标记和文字),请移至下一个标记元素并将其与第一个<PmtInfId>asd</PmtInfId>中的PmtInf与来自第二个<PmtMtd>TRF</PmtMtd>的{​​{1}}进行比较PmtInf ...如果在我们到达<PmtMtd>TRF</PmtMtd>标记之前总是完美匹配,请执行此操作。
    2. 当我们到达PmtInf时,表示第一个<CdtTrfTxInf>的第一部分与第二个<CdtTrfTxInf>的第一部分相同。此时,从第一个PmtInf的{​​{1}}部分后面的第二个PmtInf移动<CdtTrfTxInf></CdtTrfTxInf>。然后,删除第二个PmtInf部分。

所以,此时,xml看起来像这样:

<CdtTrfTxInf></CdtTrfTxInf>
  1. 现在使用第一个PmtInf部分和第三个部分重复此过程,然后使用第四个部分重复此过程。如果有几乎完美匹配,我们应该只有一个PmtInf标记,其中包含4个<?xml version="1.0" encoding="UTF-8"?> <Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"> <CstmrCdtTrfInitn> <GrpHdr> <MsgId>123</MsgId> <CreDtTm>321</CreDtTm> <NbOfTxs>10</NbOfTxs> <CtrlSum>18700.68</CtrlSum> <InitgPty> <Nm>some info</Nm> </InitgPty> </GrpHdr> <PmtInf> <!-- start --> <PmtInfId>asd</PmtInfId> <PmtMtd>TRF</PmtMtd> <BtchBookg>false</BtchBookg> <PmtTpInf> <InstrPrty>NORM</InstrPrty> <SvcLvl> <Prtry>test</Prtry> </SvcLvl> </PmtTpInf> <ReqdExctnDt>date</ReqdExctnDt> <Dbtr> <Nm>something</Nm> <PstlAdr> <AdrLine>addr 1</AdrLine> </PstlAdr> </Dbtr> <!-- end --> <CdtTrfTxInf> <PmtId> <InstrId>16082672122</InstrId> <EndToEndId>16082672122</EndToEndId> </PmtId> <Amt> <InstdAmt Ccy="RON">2159.41</InstdAmt> </Amt> <CdtrAgt> <FinInstnId> <BIC>some bic</BIC> </FinInstnId> </CdtrAgt> </CdtTrfTxInf> <CdtTrfTxInf> <PmtId> <InstrId>16082672122</InstrId> <EndToEndId>16082672122</EndToEndId> </PmtId> <Amt> <InstdAmt Ccy="RON">2159.41</InstdAmt> </Amt> <CdtrAgt> <FinInstnId> <BIC>some bic</BIC> </FinInstnId> </CdtrAgt> </CdtTrfTxInf> </PmtInf> <PmtInf> <!-- start --> <PmtInfId>qwe</PmtInfId> <PmtMtd>TRF</PmtMtd> <BtchBookg>false</BtchBookg> <PmtTpInf> <InstrPrty>HIGH</InstrPrty> <SvcLvl> <Prtry>test</Prtry> </SvcLvl> </PmtTpInf> <ReqdExctnDt>date</ReqdExctnDt> <Dbtr> <Nm>something</Nm> <PstlAdr> <AdrLine>addr 1</AdrLine> </PstlAdr> </Dbtr> <!-- end --> <CdtTrfTxInf> <PmtId> <InstrId>16082672122</InstrId> <EndToEndId>16082672122</EndToEndId> </PmtId> <Amt> <InstdAmt Ccy="RON">2159.41</InstdAmt> </Amt> <CdtrAgt> <FinInstnId> <BIC>some bic</BIC> </FinInstnId> </CdtrAgt> </CdtTrfTxInf> </PmtInf> <PmtInf> <!-- start --> <PmtInfId>asd</PmtInfId> <PmtMtd>TRF</PmtMtd> <BtchBookg>false</BtchBookg> <PmtTpInf> <InstrPrty>NORM</InstrPrty> <SvcLvl> <Prtry>test</Prtry> </SvcLvl> </PmtTpInf> <ReqdExctnDt>date</ReqdExctnDt> <Dbtr> <Nm>something</Nm> <PstlAdr> <AdrLine>addr 1</AdrLine> </PstlAdr> </Dbtr> <!-- end --> <CdtTrfTxInf> <PmtId> <InstrId>16082672122</InstrId> <EndToEndId>16082672122</EndToEndId> </PmtId> <Amt> <InstdAmt Ccy="RON">2159.41</InstdAmt> </Amt> <CdtrAgt> <FinInstnId> <BIC>some bic</BIC> </FinInstnId> </CdtrAgt> </CdtTrfTxInf> </PmtInf> </CstmrCdtTrfInitn> </Document> 标记。
  2. 如果在某些时候存在不匹配(例如,将第一个PmtInf中的PmtInf与第三个CdtTrfTxInf中的<InstrPrty>NORM</InstrPrty>进行比较时,请保留PmtInf部分原样并转到下一部分。
  3. 在我们完成将第一个<InstrPrty>HIGH</InstrPrty>与其上方的所有PmtInf进行比较后,将第二个PmtInf与第三个PmtInf进行比较并应用相同的规则,然后将第三个与{第一个......等等。
  4. 现在我可能会问太多,但这可以用XSLT完成吗?我知道我没有尝试过一件事,但我只是花了太多精力来尝试通过简单的Python字符串操作实现这一点,看起来像XSLT转换文档需要一些时间来使用语法。

    我这样调用脚本:

    PmtInfs

1 个答案:

答案 0 :(得分:2)

我对是否要发布此内容表示犹豫,因为您似乎是XSLT的初学者,这是解决复杂问题的复杂解决方案。你可能需要花些时间来解决这个问题。

这是如何运作的:

在第一遍中,我们为输入XML中的每个pmt元素生成一个PmtInf元素,并使用包含每个节点的名称/值对的字符串填充它(元素或属性) )当前PmtInf的后代。在给定的示例中,每个此类pmt元素看起来类似于:

<pmt id="idp1696">[PmtInfId:asd][PmtMtd:TRF][BtchBookg:false][PmtTpInf:NORMtest][InstrPrty:NORM][SvcLvl:test][Prtry:test][ReqdExctnDt:date][Dbtr:somethingaddr 1][Nm:something][PstlAdr:addr 1][AdrLine:addr 1]</pmt>

在下一步中,我们将Muenchian grouping应用于第一遍中生成的pmt个节点。对于每个不同的pmt节点,我们创建一个PmtInf元素并用以下内容填充:

  1. 相应PmtInf元素的内容,CdtTrfTxInf除外;

  2. 来自该组所有成员的所有CdtTrfTxInf元素的副本。

  3. XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
    xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03"
    xmlns:exsl="http://exslt.org/common"
    exclude-result-prefixes="ns0"
    extension-element-prefixes="exsl">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="pmt" match="ns0:pmt" use="." />
    <xsl:key name="PmtInf" match="ns0:PmtInf" use="generate-id()" />
    
    <xsl:variable name="input" select="/" />
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="ns0:CstmrCdtTrfInitn">
        <!-- first pass -->
        <xsl:variable name="first-pass-rtf">
            <xsl:apply-templates select="ns0:PmtInf" mode="gen-key"/>
        </xsl:variable>
        <xsl:variable name="first-pass" select="exsl:node-set($first-pass-rtf)" />
        <!-- output -->
        <xsl:copy>
            <xsl:copy-of select="ns0:GrpHdr"/>
                <!-- for each distinct pmt -->
                <xsl:for-each select="$first-pass/ns0:pmt[count(. | key('pmt', .)[1]) = 1]">
                    <xsl:variable name="id" select="@id" />
                    <xsl:variable name="ids" select="key('pmt', .)/@id" />
                    <PmtInf>
                        <!-- switch context back to XML input -->
                        <xsl:for-each select="$input">
                            <xsl:copy-of select="key('PmtInf', $id)/*[not(self::ns0:CdtTrfTxInf)]"/>
                            <xsl:copy-of select="key('PmtInf', $ids)/ns0:CdtTrfTxInf"/>
                        </xsl:for-each>
                    </PmtInf>
                </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="ns0:PmtInf" mode="gen-key">
        <pmt id="{generate-id()}">
            <xsl:apply-templates select="@*|*" mode="gen-key"/>
        </pmt>
    </xsl:template>
    
    <xsl:template match="@*|node()" mode="gen-key">
        <xsl:text>[</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>:</xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>]</xsl:text>
        <xsl:apply-templates select="@*|*" mode="gen-key"/>
    </xsl:template>
    
    <xsl:template match="ns0:CdtTrfTxInf" mode="gen-key"/>
    
    </xsl:stylesheet>