XSLT重命名&添加元素

时间:2018-03-19 05:53:13

标签: insert rename xslt-2.0

目的

我正在尝试编写一个样式表,它将对应于架构版本1的实例文档转换为架构的版本2。有大约300个元素,所以我不想写一堆模板。版本之间的绝大多数差异是一堆标准重命名,例如删除前缀,在以小写结尾的单词和相邻单词之间删除下划线等。但是我还需要适应添加和删除元素。

重命名规则

  1. 替换所有下划线字符,除非它们将相邻的大写字母分开。
  2. 删除“_of_”和“_to_Value”的所有实例。
  3. 将'_or_'的所有实例替换为'_Or _'。
  4. 为'Header_Parties'和'Header_Party'之前的'Header_'删除'Header_'EXCEPT的所有实例。它们最终应该是'HeaderParties'和'HeaderParty'。
  5. 要添加的元素

      <SentSequence> 之后
    1. <StatusCode>

      要删除的元素

      1. <Line_Order>
      2. 源XML

        <?xml version="1.0" encoding="UTF-8"?>
        <Entries>
            <Entry>
                <Entry_Number>10158271304</Entry_Number>
                <CHB_File>63475017024503000</CHB_File>
                <Traffic_File>1017271467</Traffic_File>
                <Status_Code>A</Status_Code>
                <Header_Country_of_Origin>VN</Header_Country_of_Origin>
                <Importer_or_Owner>Owner</Importer_or_Owner>
                <Entry_Total_Additions_to_Value>.00</Entry_Total_Additions_to_Value>
                <Entry_IRS_Excise_Tax>.00</Entry_IRS_Excise_Tax>
                <Entry_AD_Duties>.00</Entry_AD_Duties>
                <Entry_CV_Duties>.00</Entry_CV_Duties>
                <Header_Parties>
                    <Header_Party>
                        <Header_Party_Type>Importer</Header_Party_Type>
                    </Header_Party>
                </Header_Parties>
                <Invoices>
                    <Invoice>
                        <Invoice_Order>1</Invoice_Order>
                        <Invoice_Lines>
                            <Invoice_Line>
                                <Line_Order>1</Line_Order>
                                <Line_Quantity>685</Line_Quantity>
                                <Entry_Lines>
                                    <Entry_Line>
                                        <CBP7501_Line>1</CBP7501_Line>
                                    </Entry_Line>
                                </Entry_Lines>
                            </Invoice_Line>
                        </Invoice_Lines>
                    </Invoice>
                </Invoices>
            </Entry>
        </Entries>
        

        目标XML

        <?xml version="1.0" encoding="UTF-8"?>
        <Entries>
            <Entry>
                <EntryNumber>10158271304</EntryNumber>
                <CHB_File>63475017024503000</CHB_File>
                <TrafficFile>1017271467</TrafficFile>
                <StatusCode>A</StatusCode>
                <SentSequence>1</SentSequence>
                <CountryOrigin>VN</CountryOrigin>
                <ImporterOrOwner>Owner</ImporterOrOwner>
                <Entry_Total_Additions_to_Value>.00</Entry_Total_Additions_to_Value>
                <IRS_ExciseTax>.00</IRS_ExciseTax>
                <AD_Duties>.00</AD_Duties>
                <CV_Duties>.00</CV_Duties>
                <HeaderParties>
                    <HeaderParty>
                        <PartyType>Importer</PartyType>
                    </HeaderParty>
                </HeaderParties>
                <Invoices>
                    <Invoice>
                        <InvoiceOrder>1</InvoiceOrder>
                        <InvoiceLines>
                            <InvoiceLine>
                                <LineQuantity>685</LineQuantity>
                                <EntryLines>
                                    <EntryLine>
                                        <CBP7501Line>1</CBP7501_Line>
                                    </EntryLine>
                                </EntryLines>
                            </InvoiceLine>
                        </InvoiceLines>
                    </Invoice>
                </Invoices>
            </Entry>
        </Entries>
        

        进展至日期

        我发现了一些处理重命名的方法,从使用函数(我最初的想法)到多个模板。我找到的最有希望的方法是使用第二个样式表。我使用了从例8-9中得到的一个。在O'Reilly的XSLT Cookbook第2版中。但是我喜欢generic external但却无法使用它。我使用的那个似乎最适合处理添加和重命名。但是我很好混合,因为指定所有重命名有点效率低。在我的情况下,我在Excel电子表格中有文档,所以我用它来生成映射样式表的内容。

        样式表

        <?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:math="http://www.w3.org/2005/xpath-functions/math"
            xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
            xmlns:kn="http://us.customsbrokerage.net/kn/cb/xsd/v1.0/functions"
            xmlns:ren="http://www.ora.com/namespaces/rename"
            xmlns:cvt="my:convert"
            exclude-result-prefixes="xs math xd"
            version="3.0">
        
            <!-- Mapping stylesheet -->
            <cvt:convert>
                <element rename="true" curName="Entry_Number" new="EntryNumber"/>
                <element rename="true" curName="Traffic_File" new="TrafficFile"/>
                <element rename="true" curName="Status_Code" new="StatusCode"/>
                <element add="true" curName="Status_Code" new="SentSequence"/>
                <element rename="true" curName="Header_Country_of_Origin" new="CountryOriginCode"/>
                <element rename="true" curName="Importer_or_Owner" new="ImporterOrOwner"/>
                <element rename="true" curName="Entry_Total_Additions_to_Value" new="TotalAdditions"/>
                <element rename="true" curName="Entry_IRS_Excise_Tax" new="IRS_ExciseTax"/>
                <element rename="true" curName="Entry_AD_Duties" new="AD_Duties"/>
                <element rename="true" curName="Entry_CV_Duties" new="CV_Duties"/>
                <element rename="true" curName="Header_Parties" new="HeaderParties"/>
                <element rename="true" curName="Header_Party" new="HeaderParty"/>
                <element rename="true" curName="Header_Party_Type" new="CBP_PartyType"/>
                <element rename="true" curName="Invoice_Lines" new="InvoiceLines"/>
                <element rename="true" curName="Invoice_Line" new="InvoiceLine"/>
                <element rename="true" curName="Invoice_Order" new="InvoiceOrder"/>
                <element rename="true" curName="Line_Order" new=""/>
                <element rename="true" curName="Line_Quantity" new="LineQuantity"/>
                <element rename="true" curName="Entry_Lines" new="EntryLines"/>
                <element rename="true" curName="Entry_Line" new="EntryLine"/>
                <element rename="true" curName="CBP7501_Line" new="CBP7501Line"/>
            </cvt:convert>
            <xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="true" version="1.0"/>
        
            <xsl:template match="node()|@*">
                <xsl:copy>
                    <xsl:apply-templates select="node()|@*"/>
                </xsl:copy>
            </xsl:template>
            <xsl:template match=
                "*[name()=document('')/*/cvt:convert/element/@curName]">
                <xsl:variable name="convertNode" select="*[name()=document('')/*/cvt:convert/element/@curName]"/>
                <xsl:if test="$convertNode/@rename='true'">
                    <xsl:element name=
                        "{document('')/*/cvt:convert/element
                        [@curName=name(current())]
                        /@new}">
                    </xsl:element>
                </xsl:if>
                <xsl:if test="*[name()=document('')/*/cvt:convert/element/@curName]/@add=true()">
                    <xsl:element name=
                        "{document('')/*/cvt:convert/element
                        [@curName=name(current())]
                        /@new}">
                    </xsl:element>
                </xsl:if>
                <xsl:if test="*[name()=document('')/*/cvt:convert/element/@delete]=true()"/>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:template>
        </xsl:stylesheet>
        

        我在SO上最接近的问题似乎是here。但是,我不明白这种方法足以将它移植到我的案例中。此外,它需要XSLT 3.0或使用XSLT 2.0的两步过程。我更喜欢使用XSLT 2.0的一步过程,但如果必须,我可以去XSLT 3.0。

        解决方案附录

        对于每个人的参考,我使用XSLT 3.0方法进行以下更改。请参阅XSLT 3.0 solution changes

        • 为删除添加了delete =“true”属性,并相应地使用@new =''谓词更改了模板匹配。 (<xsl:key name="del-ref" match="element[@delete = 'true']" use="@curName"/><xsl:template match="*[key('del-ref', local-name(), $convert-map)]" priority="5"/>

        • 修改模式=“新”模板,用于在同一现有节点之后处理多个添加。添加了xsl:choose来处理有多个添加(<xsl:when test="count(key('add-ref', local-name(), $convert-map))>1">)或只有一个(<xsl:otherwise>)的情况。使用xsl:for-each指令迭代多次添加(<xsl:for-each select="key('add-ref', local-name(), $convert-map)"><xsl:element name="{./@new}"/>)。

1 个答案:

答案 0 :(得分:0)

我使用了您的映射表,但仅用于重命名,对于要添加或要删除的元素,我已直接实现了模板;我也刚刚使用了一个变量用于映射表而不是顶级元素,因为在我的视图中,在XSLT 1中更需要使用document('')在样式表中读取。

产生的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:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array"
    version="3.0">

  <xsl:param name="rename-map">
        <element rename="true" curName="Entry_Number" new="EntryNumber"/>
        <element rename="true" curName="Traffic_File" new="TrafficFile"/>
        <element rename="true" curName="Status_Code" new="StatusCode"/>
        <element rename="true" curName="Header_Country_of_Origin" new="CountryOriginCode"/>
        <element rename="true" curName="Importer_or_Owner" new="ImporterOrOwner"/>
        <element rename="true" curName="Entry_Total_Additions_to_Value" new="TotalAdditions"/>
        <element rename="true" curName="Entry_IRS_Excise_Tax" new="IRS_ExciseTax"/>
        <element rename="true" curName="Entry_AD_Duties" new="AD_Duties"/>
        <element rename="true" curName="Entry_CV_Duties" new="CV_Duties"/>
        <element rename="true" curName="Header_Parties" new="HeaderParties"/>
        <element rename="true" curName="Header_Party" new="HeaderParty"/>
        <element rename="true" curName="Header_Party_Type" new="CBP_PartyType"/>
        <element rename="true" curName="Invoice_Lines" new="InvoiceLines"/>
        <element rename="true" curName="Invoice_Line" new="InvoiceLine"/>
        <element rename="true" curName="Invoice_Order" new="InvoiceOrder"/>
        <element rename="true" curName="Line_Quantity" new="LineQuantity"/>
        <element rename="true" curName="Entry_Lines" new="EntryLines"/>
        <element rename="true" curName="Entry_Line" new="EntryLine"/>
        <element rename="true" curName="CBP7501_Line" new="CBP7501Line"/>
  </xsl:param>

  <xsl:key name="map-ref" match="element[@rename = 'true']" use="@curName"/>

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

  <xsl:template match="Line_Order"/>

  <xsl:template match="Status_Code">
      <xsl:next-match/>
      <SentSequence>1</SentSequence>
  </xsl:template>

  <xsl:template match="*[key('map-ref', local-name(), $rename-map)]">
      <xsl:element name="{key('map-ref', local-name(), $rename-map)/@new}">
          <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFukv8t联机,如果您定位XSLT 2处理器,请使用身份转换模板替换上面使用的xsl:mode指令

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

如果您想从该映射数据中添加和删除,您可以使用

<?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"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:param name="rename-map">
       <element rename="true" curName="Entry_Number" new="EntryNumber"/>
        <element rename="true" curName="Traffic_File" new="TrafficFile"/>
        <element rename="true" curName="Status_Code" new="StatusCode"/>
        <element add="true" curName="Status_Code" new="SentSequence"/>
        <element rename="true" curName="Header_Country_of_Origin" new="CountryOriginCode"/>
        <element rename="true" curName="Importer_or_Owner" new="ImporterOrOwner"/>
        <element rename="true" curName="Entry_Total_Additions_to_Value" new="TotalAdditions"/>
        <element rename="true" curName="Entry_IRS_Excise_Tax" new="IRS_ExciseTax"/>
        <element rename="true" curName="Entry_AD_Duties" new="AD_Duties"/>
        <element rename="true" curName="Entry_CV_Duties" new="CV_Duties"/>
        <element rename="true" curName="Header_Parties" new="HeaderParties"/>
        <element rename="true" curName="Header_Party" new="HeaderParty"/>
        <element rename="true" curName="Header_Party_Type" new="CBP_PartyType"/>
        <element rename="true" curName="Invoice_Lines" new="InvoiceLines"/>
        <element rename="true" curName="Invoice_Line" new="InvoiceLine"/>
        <element rename="true" curName="Invoice_Order" new="InvoiceOrder"/>
        <element rename="true" curName="Line_Order" new=""/>
        <element rename="true" curName="Line_Quantity" new="LineQuantity"/>
        <element rename="true" curName="Entry_Lines" new="EntryLines"/>
        <element rename="true" curName="Entry_Line" new="EntryLine"/>
        <element rename="true" curName="CBP7501_Line" new="CBP7501Line"/>
  </xsl:param>

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

  <xsl:key name="map-ref" match="element[@rename = 'true']" use="@curName"/>
  <xsl:key name="new-ref" match="element[@add = 'true']" use="@curName"/>

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

  <xsl:template match="*[key('map-ref', local-name(), $rename-map)[@new = '']]" priority="5"/>

  <xsl:template match="Status_Code">
      <xsl:next-match/>
      <SentSequence>1</SentSequence>
  </xsl:template>

  <xsl:template match="*[key('map-ref', local-name(), $rename-map)]">
      <xsl:element name="{key('map-ref', local-name(), $rename-map)/@new}">
          <xsl:apply-templates/>
      </xsl:element>
      <xsl:apply-templates select=".[key('new-ref', local-name(), $rename-map)]" mode="new"/>
  </xsl:template>

  <xsl:template match="*" mode="new">
      <xsl:element name="{key('new-ref', local-name(), $rename-map)/@new}">1</xsl:element>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFukv8t/1