Используйте XSLT для изменения корневого элемента и пространства имен, но сохраните другие общие пространства имен

Я пытаюсь преобразовать документ XML в документ с новым корневым элементом, но который имеет много других общих пространств имен элементов. У меня возникли проблемы с преобразованием пространства имен.

Это входной XML:

<CreditNote
      xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
      xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
      xmlns = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2">
   <cbc:CustomizationID>12345</cbc:CustomizationID>
   <cbc:ProfileID>67890</cbc:ProfileID>
   <cbc:ID>abcdef</cbc:ID>
   <cac:BillingReference>
      <cac:InvoiceDocumentReference>
         <cbc:ID>ghijk</cbc:ID>
         <cbc:IssueDate>2024-08-05</cbc:IssueDate>
      </cac:InvoiceDocumentReference>
   </cac:BillingReference>
</CreditNote>

Это выходной XML, который мне нужен:

<?xml version = "1.0" encoding = "UTF-8"?>
<ubl:Invoice xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
             xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
             xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
             xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
   <cbc:ID>abcdef</cbc:ID>
   <cac:BillingReference>
      <cac:InvoiceDocumentReference>
         <cbc:ID>ghijk</cbc:ID>
      </cac:InvoiceDocumentReference>
   </cac:BillingReference>
</ubl:Invoice>

Корневой узел (или пространство имен) предназначен для переключения с «urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2» на «urn:oasis:names:specification:ubl:schema:xsd:Invoice-2». ", при этом разделяя общие пространства имен: cac и cbc.

Я использую XSLT 3.0 и SAXON-HE 12.4. Это мой XLST. Я позаимствовал несколько советов из этого поста: XSLT для переименования квалифицированного корневого элемента, сохранение других пространств имен

<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet version = "3.0"
                xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
                xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
                xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
                xmlns:cn = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"
                xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
                xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
                exclude-result-prefixes = "cn">

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

   <xsl:template match = "/cn:CreditNote">
      <ubl:Invoice
            xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
            xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
            xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
            xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
         <xsl:apply-templates select = "cbc:ID"/>
         <xsl:apply-templates select = "cac:BillingReference"/>
      </ubl:Invoice>
   </xsl:template>

   <!-- 
      Identity template, provides default behavior that copies all content into 
      the output Do not copy namespace attributes into elements.
    -->
   <xsl:template match = "@* | node()">
      <xsl:copy copy-namespaces = "no">
         <xsl:apply-templates select = "@* | node()"/>
      </xsl:copy>
   </xsl:template>

   <!-- 
      Copy all elements from the CreditNote namespace, but change the namespace 
      to the default namespace (Invoice).
    -->
   <xsl:template match = "cn:*">
      <xsl:element name = "{local-name()}">
         <xsl:apply-templates select = "@*|node()"/>
      </xsl:element>
   </xsl:template>

   <xsl:template match = "*/cac:BillingReference">
      <xsl:copy>
         <xsl:copy-of select = "namespace::*[not(. = ('urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2'))]"/>
         <xsl:apply-templates select = "cac:InvoiceDocumentReference"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

Это выходной XML, который я получаю:

<?xml version = "1.0" encoding = "UTF-8"?>
<ubl:Invoice xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
             xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
             xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
             xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
   <cbc:ID>abcdef</cbc:ID>
   <cac:BillingReference xmlns = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2">
      <cac:InvoiceDocumentReference>
         <cbc:ID>ghijk</cbc:ID>
         <cbc:IssueDate>2024-08-05</cbc:IssueDate>
      </cac:InvoiceDocumentReference>
   </cac:BillingReference>
</ubl:Invoice>

Я понимаю, что XSLT пытается сохранить исходные пространства имен, но мне неясно, почему мой XSLT не решил эту проблему.

  1. Как запретить вывод xmlns = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2" в выбранных шаблонах?
  2. Почему шаблон cac:BillingReference не соответствует? (Его следовало обрезать cbc:IssueDate.)

Обновление — четверг, 29 августа 2024 г., 16:53:09.

Примечание для всех, кому этот пост полезен: не используйте xsl:copy при попытке вывести тот же элемент, но при этом изменить исходное пространство имен. XSLT ниже по-прежнему выводит старое пространство имен. XSLT:

<xsl:template match = "cac:BillingReference">
   <xsl:copy>
      <xsl:apply-templates select = "cac:InvoiceDocumentReference"/>
   </xsl:copy>
</xsl:template>

Вывод содержит старое пространство имен:

<cac:BillingReference xmlns = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2">
   <cac:AdditionalDocumentReference>
      <cbc:ID>ghijk</cbc:ID>
   </cac:AdditionalDocumentReference>
</cac:BillingReference>

Поэтому мне нужно напрямую вывести имя элемента XML или использовать некоторые другие директивы XSL для выполнения этой работы.

<xsl:template match = "cac:BillingReference">
   <cac:BillingReference>
      <xsl:apply-templates select = "*"/>
   </cac:BillingReference>
</xsl:template>

xsl:element — очень хороший инструмент для шаблонов, в которых вы сопоставляете несколько элементов ввода, поэтому вам не обязательно знать имя элемента.

<xsl:template match = "cac:PostalAddress|cac:Address">
   <xsl:element name = "{name()}">
      <xsl:apply-templates select = "*"/>
   </xsl:element>
</xsl:template>

Почему ваш вывод включает в себя как xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-‌​2, так и xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invo‌​ice-2"?

michael.hor257k 28.08.2024 08:08

Наверное, не обязательно. В какой-то момент я подумал, что важно определить пространство имен по умолчанию, чтобы XSLT не мог решить, что это urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2.

Robert Mark Bram 28.08.2024 09:17

Это не имеет смысла. Пространство имен по умолчанию применяется только к элементам без префиксов, и в выводе их нет.

michael.hor257k 28.08.2024 11:06

Я согласен с задним числом. Оглядываясь назад, все, что у меня было, — это соломинка, за которую я хватался.

Robert Mark Bram 29.08.2024 00:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я думаю, что достаточно следующего:

<xsl:stylesheet version = "3.0"
                xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
                xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
                xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
                xmlns:cn = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"
                xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
                xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
                exclude-result-prefixes = "cn">

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

   <xsl:template match = "/cn:CreditNote">
      <ubl:Invoice
            xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
            xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
            xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
            xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
         <xsl:apply-templates select = "cbc:ID"/>
         <xsl:apply-templates select = "cac:BillingReference"/>
      </ubl:Invoice>
   </xsl:template>

   <!-- 
      Identity template, provides default behavior that copies all content into 
      the output Do not copy namespace attributes into elements.
    -->
   <xsl:template match = "@* | node()">
      <xsl:copy copy-namespaces = "no">
         <xsl:apply-templates select = "@* | node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match = "cbc:IssueDate"/>

</xsl:stylesheet>

Пример саксонской скрипки HE.

Спасибо большое - это то, что мне было нужно.

Robert Mark Bram 28.08.2024 11:02

Это copy-namespaces = "no" и есть ключевой трюк, не так ли?

Conal Tuohy 29.08.2024 10:03

Я подозреваю, что все, что вам нужно сделать, это:

<xsl:stylesheet version = "3.0" 
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:cac = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<xsl:output method = "xml" version = "1.0" encoding = "UTF-8" indent = "yes"/>

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

<xsl:template match = "cbc:CustomizationID | cbc:ProfileID | cbc:IssueDate"/>

</xsl:stylesheet>

Демо здесь.

Без указания имен в <ubl:Invoice> это изменило вывод так, что я получил: <ubl:Invoice xmlns:cbc = "..." xmlns:ubl = "...">' but then later: <cac:BillingReference xmlns:cac = "..." ..` а это не то, что я хотел, хотя я понимаю это исключительно с точки зрения XML сформировалось, это может не иметь значения.

Robert Mark Bram 28.08.2024 10:44

Это не тот результат, который я получаю — см. демо-версию по добавленной ссылке.

michael.hor257k 28.08.2024 11:05

Спасибо за это @michael.hor257k. Скрипки блестящие! Я вижу, что это связано с платформой, которую я использую. Когда я выбираю Saxon 12.5 HE Java в скрипке (я использую 12.4), я получаю результат, который вижу в своем собственном приложении. Когда я запускаю его на Saxon 10 .NET, все происходит так, как вы предложили.

Robert Mark Bram 29.08.2024 01:05

Хм. Я все еще получаю другой результат, используя ту же скрипку и тот же процессор.

michael.hor257k 29.08.2024 07:45

Ой. Я исправляюсь. Я тестировал свой собственный XSLT. Да, вы правы — мне тоже не нужно указывать пространства имен на ubl:Invoice. Хороший.

Robert Mark Bram 29.08.2024 08:52

Другие вопросы по теме