将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点

Posted

技术标签:

【中文标题】将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点【英文标题】:Pass namespaces from java to xslt and use params from java as node in xslt 【发布时间】:2019-08-27 03:32:11 【问题描述】:

我有一个 xslt 文件,可以使用 apache-fop 将 xml 文件转换为 pdf。但是我的 xslt 中没有关于命名空间的所有信息。这取决于xml。我可以在 java 中分析 xml 文档并从 xml 中获取所有命名空间。但我不知道如何将这个命名空间从 java 传递到我的 xslt 文件,以及接下来如何在 <xsl:stylesheet> 标记中声明它。有可能吗?

我无法粘贴我的原始 xslt 和 xml,因为它包含敏感数据,但我准备了示例文件来显示我的问题:

    <?xml version="1.0" encoding="UTF-8"?>
<ns0:OtherCompany xmlns:ns8="http://www.company.com/schema/SF/definition/type/test"  xmlns:ns0="http://www.company.com/schema/SF/definition/type/a" xmlns:ns7="http://www.company.com/schema/SF/definition/type/b" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <ns0:Header>
      <ns8:From>2018-01-01</ns8:From>
      <ns8:To>2018-12-31</ns8:To>
      <ns8:CheckDate>2019-03-28</ns8:CheckDate>
      <ns7:Code sysCode="1">Report</ns7:Code>
      <ns7:Type>1</ns7:Type>
   </ns0:Header>
   <ns0:Changes>
      <ns7:I>
         <ns8:AmountA>1499142.61</ns8:AmountA>
         <ns8:AmountB>54979.16</ns8:AmountB>
      </ns7:I>
      <ns7:II>
         <ns8:AmountA>3398983.19</ns8:AmountA>
         <ns8:AmountB>1499142.61</ns8:AmountB>
      </ns7:II>
      <ns7:III>
         <ns8:AmountA>3398983.19</ns8:AmountA>
         <ns8:AmountB>1499142.61</ns8:AmountB>
      </ns7:III>
   </ns0:Changes>
</ns0:OtherCompany>

和xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" 
        xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="fo" xmlns:ns0="http://www.company.com/schema/SF/definition/type/a" xmlns:ns7="http://www.company.com/schema/SF/definition/type/b">

    <xsl:param name="xmlPathPrefix"/>

    <xsl:template match="/">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
          <fo:layout-master-set>
            <fo:simple-page-master master-name="simpleA4" page- page- margin-top="2cm" margin-bottom="2cm" margin-left="1cm" margin-right="1cm">
              <fo:region-body region-name="xsl-region-body" margin-top=".80in" margin-bottom=".50in"/>
            </fo:simple-page-master>
          </fo:layout-master-set>
          <fo:page-sequence master-reference="simpleA4">
            <fo:flow flow-name="xsl-region-body">
                <fo:block font-size="10pt" font-family="Arial">
                  <fo:table table-layout="fixed" >                    
                    <fo:table-column column- xsl:use-attribute-sets="columnStyle"/>
                    <fo:table-column column- xsl:use-attribute-sets="columnStyle"/>
                    <fo:table-header>
                        <fo:table-row xsl:use-attribute-sets="columnStyle">
                            <fo:table-cell xsl:use-attribute-sets="centerCellStyle">
                                 <fo:block font-weight="bold">Name</fo:block>
                            </fo:table-cell>
                            <fo:table-cell xsl:use-attribute-sets="centerCellStyle">
                                 <fo:block font-weight="bold">Value</fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-header>
                    <fo:table-body>
                        <xsl:apply-templates select="$xmlPathPrefix//*[not(contains(name(), 'Content'))]"/>
                    </fo:table-body>
                  </fo:table>
                </fo:block>
            </fo:flow>
          </fo:page-sequence>
         </fo:root>
    </xsl:template>

    <xsl:template match="$xmlPathPrefix//*[not(contains(name(), 'Content'))]">  
        <fo:table-row xsl:use-attribute-sets="columnStyle">    
            <fo:table-cell>
                <fo:block>
                    <xsl:value-of select="sf:addSpaces(local-name(), sf:depth-of-node(.))"/>    
                </fo:block>
            </fo:table-cell> 
            <fo:table-cell xsl:use-attribute-sets="marginColumnStyle">
                <fo:block>
                    <xsl:choose>
                        <xsl:when test="*">
                            <xsl:value-of select="''"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="current()"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </fo:block>
            </fo:table-cell>                                    
        </fo:table-row>
    </xsl:template>

</xsl:stylesheet>

我想从 java 参数 xmlPathPrefix 传递并在 &lt;xsl:template&gt; 匹配属性的 xslt 文件中使用它

<xsl:template match="/$xmlPathPrefix/values">

或在xsl:apply-templates中选择属性

<fo:table-body>
    <xsl:apply-templates select="$xmlPathPrefix//*[not(contains(name(), 'Content'))]"/>
</fo:table-body>

但我收到以下错误:

在 test.xsl 的第 38 行第 75 列的 xsl:apply-templates/@select 中键入错误评估 ($xmlPathPrefix): XPTY0019:'/'的第一个操作数所需的项类型是node();提供的值 u"ns0:OtherCompany/ns0:Changes..." 是一个原子值

如何从 java 传递 xmlPathPrefix 并在我的 xslt 中使用它?我想将示例字符串传递为 xmlPathPrefix

"ns0:OtherCompany/ns0:Changes"

第二个问题是我的命名空间,pathPrefix可以不同,但​​是local-name总是一样的,例如可以是:

"ns0:OtherCompany/ns0:Changes"
"ns10:OtherCompany/ns15:Changes"
"companyType:OtherCompany/companyChanges:Changes"

或更多其他选项。当我有 xslt 时,我必须在 &lt;xsl:stylesheet&gt; 示例 ns0、ns10、companyType 等中声明标签。如果我不声明它,我会收到错误消息。但我不知道在我的 xml 中声明了哪些命名空间。如何将其传递给 xslt?

我通过的例子

xmlPathPrefix: "ns10:OtherCompany/ns15:Changes"

和命名空间:ns10 和 ns15

但我不知道如何到达。

【问题讨论】:

显示一个最小但完整的 XML 示例以及相应的 XSLT 和所需的 FO 输出,我相信我们可以提供帮助。 XSLT/XPath 肯定可以读取/分析 XML 文档中的名称空间,而无需知道它们或在某个名称空间中创建或复制元素。 @MartinHonnen 我用示例数据完成了我的帖子。也许你现在可以帮助我? 【参考方案1】:

您是否使用像 Saxon 9 这样的 XSLT 2 处理器?您的 XSLT 代码显示为 version="2.0"。如果您正在处理各种命名空间,那么在 XSLT/XPath 2 及更高版本中的一种方法是使用通配符 * 作为命名空间前缀,例如*:OtherCompany/*:Changes 将选择任何命名空间中的那些元素。

要参数化 select 表达式,您需要使用 XSLT 3 处理器,如 Saxon 9.8 或 9.9,以及所谓的影子属性,如 _select 和静态参数:

<?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="#all"
    version="3.0">

  <xsl:param name="prefix" as="xs:string" static="yes" select="'/*:root/*:foo'"/>

  <xsl:output indent="yes"/>

  <xsl:template match="/">
      <xsl:apply-templates _select="$prefix/*:bar"/>
  </xsl:template>

  <xsl:template match="*:bar">
      <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/ej9EGco

我认为你需要使用 Saxon 的 s9api 编程接口来设置静态参数。

【讨论】:

非常感谢,此时一切正常。我将您的回答标记为接受,但我以后可能需要帮助。

以上是关于将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点的主要内容,如果未能解决你的问题,请参考以下文章

XSLT 替换命名空间并添加新的(未使用的)命名空间

xsd:any 元素的命名空间前缀并使用 XSLT 添加命名空间前缀

单独的 xslt-replace 命名空间 uri

jquery 命名空间:如何将默认选项从一种方法传递给子序列方法?

重命名元素的 XSLT 问题——更改命名空间

xslt 中的 XML 命名空间处理