Я пытаюсь преобразовать документ 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 не решил эту проблему.
xmlns = "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-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>
Наверное, не обязательно. В какой-то момент я подумал, что важно определить пространство имен по умолчанию, чтобы XSLT не мог решить, что это urn:oasis:names:specification:ubl:schema:xsd:CreditNote-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>
Спасибо большое - это то, что мне было нужно.
Это copy-namespaces = "no"
и есть ключевой трюк, не так ли?
Я подозреваю, что все, что вам нужно сделать, это:
<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 сформировалось, это может не иметь значения.
Это не тот результат, который я получаю — см. демо-версию по добавленной ссылке.
Спасибо за это @michael.hor257k. Скрипки блестящие! Я вижу, что это связано с платформой, которую я использую. Когда я выбираю Saxon 12.5 HE Java в скрипке (я использую 12.4), я получаю результат, который вижу в своем собственном приложении. Когда я запускаю его на Saxon 10 .NET, все происходит так, как вы предложили.
Хм. Я все еще получаю другой результат, используя ту же скрипку и тот же процессор.
Ой. Я исправляюсь. Я тестировал свой собственный XSLT. Да, вы правы — мне тоже не нужно указывать пространства имен на ubl:Invoice
. Хороший.
Почему ваш вывод включает в себя как
xmlns = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2
, так иxmlns:ubl = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
?