0

我们将 XSL 样式表应用于许多具有不同结构和标签的 XML 文件。我们希望对所有文件使用单个 XSL 样式表,如果添加了具有新内容结构的 XML 文件,我们可以在其中简单地添加新的 xpath。

(我可能会补充一点,这是与 Apache 的 Solr 一起使用的,输出文档需要以某种方式显示。)

到目前为止,我们已经成功编写了复制各个字段的代码,如下所示:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt" xmlns:exslt="http://exslt.org/common" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" xalan:indent-amount="4" omit-xml-declaration="yes"/>
<xsl:template match="/">
    <xsl:param name="fileName" />
    <xsl:param name="fileURI" />
    <xsl:param name="timeCreatedLong" />
<add>
    <doc>
        <!-- REQUIRED FIELDS. DO NOT CHANGE -->
            <field name="fileName"><xsl:value-of select="$fileName" /></field>
            <field name="fileURI"><xsl:value-of select="$fileURI" /></field>
            <field name="timeCreatedLong"><xsl:value-of select="$timeCreatedLong" /></field>
        <!-- //END OF REQUIRED FIELDS -->

        <!-- DSV INTERNAL XML -->
            <!-- Consignment Identifiers -->
            <field name="consignmentIdentifiers"><xsl:value-of select="//consignmentlist/consignment/consignmentId" /></field>
            <field name="consignmentIdentifiers"><xsl:value-of select="//consignmentlist/consignment/references/reference[@type = 'consignment_number']/value" /></field>
            <!-- //Consignment Identifiers -->

            <!-- Transport company information -->
            <field name="carrier"><xsl:value-of select="//transport/transportservice/carriername" /></field>
            <field name="carrierService"><xsl:value-of select="//transport/transportservice/carrierservicename" /></field>
            <field name="transportMode"><xsl:value-of select="//transport/transportservice/transportmode" /></field>
            <!-- //Transport company information -->
        <!-- //DSV INTERNAL XML -->
        

        
        <!-- POSTEN NORDIC LOGISTICS ORDER.XML -->
            <!-- Consignment Identifiers -->
            <field name="consignmentIdentifiers"><xsl:value-of select="//TransportJob/Consignment/@consignmentId" /></field>
            <!-- //Consignment Identifiers -->

            <!-- Transport company information -->
            <field name="definedBy"><xsl:value-of select="//TransportJob/@definedBy" /></field>
            <field name="carrier"><xsl:value-of select="//TransportJob/@profile" /></field>
            <!-- //Transport company information -->
        <!-- //POSTEN NORDIC LOGISTICS ORDER.XML -->
    </doc>
</add>
</xsl:template>

</xsl:stylesheet>

根据处理的文件结构,输出看起来像这样:

<add>
<doc>
    <field name="fileName">00373323993931432015_BOOKING.INTERNALXML</field>
    <field name="fileURI">/usr/dropbox/Dropbox/shared/file-search/00373323993931432015_BOOKING.INTERNALXML</field>
    <field name="timeCreatedLong">1377507872000</field>
    <field name="consignmentIdentifiers"/>
    <field name="consignmentIdentifiers">00373323993931432015</field>
    <field name="carrier">DSV</field>
    <field name="carrierService">DSV Mypack</field>
    <field name="transportMode">ROAD</field>
    <field name="consignmentIdentifiers"/>
    <field name="definedBy"/>
    <field name="carrier"/>
</doc>
</add>

如您所见,我们有一些空的/自动关闭的元素,我们希望在将其发送到我们的 Solr 服务器之前将其删除。

所以真正的问题是,有没有办法在应用这个 XSL 之后删除生成的空标签?如上所述,我们希望这在同一个 XSL 文件中完成。

4

3 回答 3

1

一个改进的建议是使用几个通用模板来匹配元素或属性,但它需要一个参数,该参数可以设置为您希望输出的字段的“名称”。

第一个模板实际上会输出字段元素,相应地设置名称属性

<xsl:template match="*|@*">
    <xsl:param name="fieldName" />
    <field name="{$fieldName}">
       <xsl:value-of select="." />
    </field>
</xsl:template>

另一个将用于忽略这些没有值的元素或属性:

<xsl:template match="*[normalize-space()='']|@*[normalize-space()='']" />

(请注意,更具体的模板(带有 Xpath 表达式检查空字符串的模板)将在此处优先于非特定模板。)

然后,而不是这样写:

<field name="consignmentIdentifiers">
    <xsl:value-of select="//consignmentlist/consignment/consignmentId" />
</field>

你会写这个

<xsl:apply-templates select="//consignmentlist/consignment/consignmentId">
    <xsl:with-param name="fieldName" select="'consignmentIdentifiers'" />
</xsl:apply-templates>

对于您希望输出的所有其他字段也是如此。因此,您不必担心围绕每个语句编写xsl:if语句。这只是对您目前正在做的事情的轻微改变。

编辑:如果你真的想将 XSLT 应用到它自己的输出中......

那么做到这一点的方法是使用“两遍变换”。理想情况下,您将在此处使用两个 XSLT,但如果您想使用一个,然后是“第一遍”而不是简单地输出新元素,您可以将现有代码包装在一个变量中

<xsl:variable name="HereBeDragons">
   <add>
      <doc>
          <field ...
      </doc>
   </add>
</xsl:variable>

因此,您现在有一个包含当前输出的变量,并带有空标签。现在,如果您使用的是 XSLT 2.0,您只需执行此操作即可开始为变量中的元素查找模板匹配项

<xsl:apply-templates select="$HereBeDragons/*"/>

但是在 XSLT 1.0 中,您可能会收到关于它不是节点集的消息。在 XSLT 1.0 中,该变量实际上存储了一个“结果树片段”,需要将其转换为节点集以允许使用模板。看起来你在这里使用 EXSLT,所以你应该能够做到这一点,在这种情况下

<xsl:apply-templates select="exslt:node-set($HereBeDragons)/*" />

现在,已经开始在变量上应用模板,您可以添加模板来根据需要处理数据。您将拥有一个用于标识模板的模板

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

还有一个,忽略你的空字段

<xsl:template match="field[normalize-space()='']" />

但要小心,这些模板将适用于第一遍和第二遍。如果您想要一个模板来匹配在第二遍中表现不同的特定元素,您可能需要使用模板上的mode属性来区分它们。

当然,以这种方式进行两次转换的效率并不高,无论是在内存方面还是在速度方面,这就是为什么在原始 XSLT 中添加逻辑以首先不输出空标签的原因。

于 2013-08-27T13:11:18.597 回答
0

您可以在 XSLT 中添加仅在源不为空时才创建元素的检查。例如对于carrier您可以执行的字段:

<xsl:if test="not(//transport/transportservice/carriername='')">
 <field name="carrier">
   <xsl:value-of select="//transport/transportservice/carriername" />
 </field>   
</xsl:if>

如果你这样做,你的输出中不会出现空字段。

编辑:如果你想检查输出,有一种非常有效的方法可以使用身份规则:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

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

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

<xsl:template match="field[normalize-space()='']"/>

</xsl:stylesheet>
于 2013-08-27T08:41:55.053 回答
0

假设 XSLT 2.0(你还没有说),我会这样做:

<xsl:sequence select="
   f:field('carrier', //transport/transportservice/carriername),
   f:field('carrierService', //transport/transportservice/carrierservicename),
   f:field('transportMode', //transport/transportservice/transportmode),
   ..."/>

f:field 定义为

<xsl:function name="f:field" as="element(field)?">
  <xsl:param name="name" as="xs:string"/>
  <xsl:param name="value" as="xs:string?"/>
  <xsl:if test="$value">
    <field name="{$name}">
      <xsl:value-of select="$value"/>
    </field>
  </xsl:if>
</xsl:function>
于 2013-08-27T09:29:21.197 回答