2016-08-17 8 views
1

Я новичок в XSLT и споткнулся об обработке пространства имен котлов.XSLT 2.0 гибкий со стандартным или различным префиксным пространством имен того же URI

Я следующий XSLT, где целью является просто переименовать один элемент:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" > 
    <xsl:strip-space elements="*" /> 

    <!-- element template that copies over elements --> 
    <xsl:template match="*"> 
     <xsl:element name="{name()}"> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:element> 
    </xsl:template> 

    <!-- attribute template to copy attributes over --> 
    <xsl:template match="@*"> 
     <xsl:copy> 
      <xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute> 
     </xsl:copy> 
    </xsl:template> 

    <!-- "other" template to copy the rest of the nodes --> 
    <xsl:template match="comment() | text() | processing-instruction()"> 
     <xsl:copy/> 
    </xsl:template> 

    <!-- Rename an element --> 
    <xsl:template match="BOPPolicyQuoteInqRq/RqUID" > 
     <xsl:element name="RqUUID"> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

Преобразуя следующий XML работает, как ожидалось:

<ACORD xmlns="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/"> 
    <InsuranceSvcRq> 
    <BOPPolicyQuoteInqRq> 
     <RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</RqUID>  
    </BOPPolicyQuoteInqRq> 
    </InsuranceSvcRq> 
</ACORD> 

Однако это семантически эквивалентно XML терпит неудачу:

<bloat:ACORD xmlns:bloat="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/"> 
    <bloat:InsuranceSvcRq> 
    <bloat:BOPPolicyQuoteInqRq> 
     <bloat:RqUID>E2BA6308-62D5-43AC-B8C1-7616FDFE9C98</bloat:RqUID>  
    </bloat:BOPPolicyQuoteInqRq> 
    </bloat:InsuranceSvcRq> 
</bloat:ACORD> 

Ошибка, которую я получаю:

Caused by: net.sf.saxon.trans.XPathException: Undeclared prefix in element name: bloat 
    at net.sf.saxon.expr.instruct.ComputedElement.getElementName(ComputedElement.java:429) 
    at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:388) 
    at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:371) 
    at net.sf.saxon.expr.instruct.Template.applyLeavingTail(Template.java:239) 
    at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1056) 
    at net.sf.saxon.trans.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:65) 
    at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:1044) 
    at net.sf.saxon.Controller.transformDocument(Controller.java:2088) 
    at net.sf.saxon.Controller.transform(Controller.java:1911) 
    at org.apache.camel.builder.xml.XsltBuilder.process(XsltBuilder.java:141) 
    at org.apache.camel.impl.ProcessorEndpoint.onExchange(ProcessorEndpoint.java:103) 
    at org.apache.camel.component.xslt.XsltEndpoint.onExchange(XsltEndpoint.java:121) 
    at org.apache.camel.impl.ProcessorEndpoint$1.process(ProcessorEndpoint.java:71) 
    at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61) 
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:141) 
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:460) 
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190) 
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:121) 
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:83) 
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190) 
    at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62) 
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190) 
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:109) 
    at org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:68) 
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:412) 
    at org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:380) 
    at org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:270) 
    at org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:380) 
    at org.apache.camel.impl.ProducerCache.send(ProducerCache.java:221) 
    at org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:124) 
    at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:137) 
    ... 32 more 

Оказывается, даже если эти XMLs семантически эквивалентны насколько XML-спецификации будут иметь дело, то XSLT трансформатор становится повесил трубку, потому что один говорит префикс, а другой не (я бы тоже рискнул сказать, он будет зависеть, если в нем будет префикс «foo», а один - «bar»).

Я в состоянии, когда я не могу заставить клиента, передающего мне xml, объявить определенный префикс или пространство имен определенным образом. Я также не могу гарантировать, что они не решат использовать другой псевдоним префикса завтра.

Мое понимание объявления атрибута xpath-default-namespace состояло в том, что он передал трансформатору xslt, какой URI пространства имен будет связан с всем документом, независимо от того, будет ли он объявлен как префикс по умолчанию, префикс с псевдонимом «bloat» или даже префикс с псевдонимом «rainbowunicorns».

Что именно делает атрибут xpath-default-namespace и как я могу написать гибкий XSLT, который может изящно обрабатывать любое количество семантически эквивалентных пространств имен независимо от того, какой вкус объявления пространства имен принимает клиент?

функции при необходимости: Camel 2.16.2 Saxon-HE 9.5.1-8

Обновлено преобразование, которое работает как с XMLs (любезно Мартина Honnen):

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" > 
    <xsl:strip-space elements="*" /> 

    <!-- element template that copies over elements --> 
    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()" /> 
     </xsl:copy> 
    </xsl:template> 

    <!-- "other" template to copy the rest of the nodes --> 
    <xsl:template match="comment() | processing-instruction()"> 
     <xsl:copy/> 
    </xsl:template> 

    <!-- Rename an element --> 
    <xsl:template match="BOPPolicyQuoteInqRq/RqUID" > 
     <xsl:element name="RqUUID" namespace="{namespace-uri()}"> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

ответ

2

Заменить

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

<!-- attribute template to copy attributes over --> 
<xsl:template match="@*"> 
    <xsl:copy> 
     <xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute> 
    </xsl:copy> 
</xsl:template> 

по

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

как то, как вы копировать любые пространства имен в объеме, а затем использовать

<xsl:template match="BOPPolicyQuoteInqRq/RqUID" > 
    <xsl:element name="{QName(namespace-uri(), if (prefix-from-QName(node-name(.))) then concat(prefix-from-QName(node-name(.)),':', 'RqUUID') else 'RqUUID')}" namespace="{namespace-uri()}"> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:element> 
</xsl:template> 

С некоторыми переменными, чтобы сохранить код читаемым всю таблицу стилей становится

<xsl:transform 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0" 
    xpath-default-namespace="http://www.ACORD.org/standards/PC_Surety/ACORD1/xml/" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs"> 

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

    <xsl:template match="BOPPolicyQuoteInqRq/RqUID"> 
     <xsl:variable name="new-local-name" as="xs:string" select="'RqUUID'"/> 
     <xsl:variable name="prefix" select="prefix-from-QName(node-name(.))"/> 
     <xsl:variable name="new-name" as="xs:string" select="if ($prefix) then concat($prefix,':', $new-local-name) else $new-local-name"/> 
     <xsl:element name="{QName(namespace-uri(), $new-name)}" namespace="{namespace-uri()}"> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:element> 
    </xsl:template> 

</xsl:transform> 
+0

Этот код определенно работает, вы были бы готовы предоставить объяснение, почему каждый из изменений, которые вы предложили закончились работа? Sidenote: Мне также пришлось удалить | text() моего «другого» правила, в сочетании с этими изменениями, он сделал неоднозначный набор правил (трансформатор xslt дал действительно ошибку, которая помогла мне исправить ее самостоятельно). Просто хочу узнать _why_ – Russ

+0

Думая о нем снова, предложение выше, очевидно, потерпит неудачу с пространством имен по умолчанию на входе, так как построенное имя будет ': RqUUID'. Мне нужно попробовать и проверить, как писать код, который работает с обоими типами ввода. –

+0

Что касается объяснения, в основном, если вы хотите скопировать узлы без изменений, то обычно рекомендуется начинать с шаблона, который я предложил сначала вместо двух (соответственно три, я пропустил третий), которые вы пробовали, это более компактный способ и 'xsl: copy' скопирует любое пространство имен в пространстве, в то время как ваши попытки с помощью' xsl: element' этого не делают. –

1

Первое слово по диагностике: это не имеет смысла печатать трассировку стека Java, когда вы получаете динамическую ошибку в таблице стилей. Saxon сообщает об ошибке зарегистрированному ErrorListener, а стандартный ErrorListener производит диагностику, которая предназначена для использования автором стилей, сообщая вам, где в XSLT произошел сбой, и трассировка стека уровня XSLT, показывающая, откуда был вызван этот код из , Если вы не видите эту диагностику, вы делаете что-то неправильно.

Во-вторых, почему это не получилось? Вы делаете

<xsl:element name="{name()}"> 

и значение имени() что-то вроде bloat:RqUID. Правила для XSL: элемент говорит:

[ERR XTDE0830] В случае XSL: инструкция элемента без атрибута пространства имен, это неустранимая динамическая ошибка, если эффективного значения имени атрибута является QName, префикс которого не , объявленный в объявлении пространства имен в области для инструкции xsl: element .

Если вы хотите, чтобы скопировать имя без изменений, используйте <xsl:copy>, или если вы предпочитаете, <xsl:element name="{name()}" namespace="{namespace-uri()}">

+0

Ничего себе не ожидал ответа от автора книги, которую я купил дважды (собака буквально съела обложку моей первой). Диагностика. Я вижу более подробную информацию о трассировке стека, в том числе строку и столбец, в которой она не выполнялась в xslt, непризнанный символ и т. Д. Однако это, как правило, ошибки времени компиляции в сравнении с временем выполнения. Похоже, я делаю что-то неправильно. Любые предложения, которые вы можете указать мне на получение лучшей диагностики времени выполнения в следующий раз, будут оценены. Это может быть верблюжьей вещью, так как мы используем верблюда для его полезной интеграции. – Russ

+0

Также спасибо за описание ошибки в моем правиле идентификации. Теперь я вижу, что использование элемента xsl: в этом случае было неправильным выбором по сравнению с xsl: copy, поскольку желательно сохранить исходные префиксы пространства имен (если они есть). Меня вынуждают охотиться и клевать в книге, вместо того, чтобы читать спереди назад (мои предпочтения) из-за давления на временную шкалу проекта. Когда я получаю время, чтобы сесть и прочитать для понимания, а не поставить график, у меня не будет таких вопросов noobish :) – Russ

 Смежные вопросы

  • Нет связанных вопросов^_^