Палец вверх 0
Перевод
Перевод

XSLT переименовать и добавить элементы

Цель

Я пытаюсь написать таблицу стилей, которая преобразует документы экземпляров, соответствующие версии 1 схемы, в версию 2 схемы. Есть около 300 элементов, поэтому я не хочу писать кучу шаблонов. Подавляющее большинство различий между версиями представляют собой набор стандартных переименований, таких как удаление префиксов, удаление символов подчеркивания между словом, заканчивающимся строчными буквами, и соседним словом и т. Д. Однако мне также необходимо учитывать добавление и удаление элементов.

Правила переименования

  1. Замените все символы подчеркивания, если они не разделяют соседние заглавные буквы.
  2. Удалите все экземпляры _of_ и _to_Value.
  3. Замените все экземпляры _or_ на _Or_.
  4. Удалите все экземпляры 'Header_', КРОМЕ для 'Header_', предшествующего 'Header_Parties' и 'Header_Party'. Они должны заканчиваться как 'HeaderParties' и 'HeaderParty'.

Элементы для добавления

  1. <SentSequence> после <StatusCode>

Элементы для удаления

  1. <Line_Order>

Исходный 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. в кулинарной книге О'Рейли XSLT, 2-е издание. Однако мне понравился внешний внешний вид, но я не смог заставить его работать. Тот, который я использовал, кажется наиболее подходящим для обработки дополнений и переименований. Однако я согласен со смесью, так как указание всех переименований немного неэффективно. В моем случае у меня есть документация в электронной таблице 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, кажется, здесь . Однако я не понимаю подход достаточно, чтобы перенести его на мой случай. Кроме того, требуется XSLT 3.0 или двухэтапный процесс с использованием XSLT 2.0. Я бы предпочел одношаговый процесс с использованием XSLT 2.0, но я мог бы перейти на XSLT 3.0, если нужно.

Дополнение к решению

Для всеобщего сведения я использовал подход XSLT 3.0 с изменениями ниже. См. Изменения решения XSLT 3.0 .

  • Добавил атрибут 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}"/> ).

rename insert xslt-2.0
задан jrpartridge 19 мар. 2018 г., 8:53:13
источник

1 ответ

Решение 0
Перевод
Перевод

Я использовал вашу таблицу сопоставления, но только для переименования, для добавления или удаления элементов. Я реализовал шаблоны напрямую; также я только что использовал переменную для таблицы сопоставления вместо элемента верхнего уровня, поскольку чтение в таблице стилей с document('') является техникой, более необходимой в XSLT 1, на мой взгляд.

Результирующий 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: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

ответ дан Martin Honnen 19 мар. 2018 г., 13:09:23
источник