正确编码 XSLT 样式表的问题

QNimbus

我对 XSLT 还很陌生,但我认为我已经掌握了基础知识。但是,我已经讨论了下面的问题一段时间了,但似乎找不到可行的解决方案。任何帮助将不胜感激。

这是我的(简化的)源 XML 文档:

<?xml version="1.0" encoding="UTF-8"?>
<domain_objects>
    <gateway id='8b33703f411e4fbba99884225a35d11c'>
        <created_date>2018-10-30T12:51:41.555+01:00</created_date>
        <modified_date>2019-09-16T20:06:09.570+02:00</modified_date>
        <enabled>true</enabled>
        <vendor_name>Plugwise</vendor_name>
        <vendor_model>smile_open_therm</vendor_model>
        <hardware_version>AME Smile 2.0 board</hardware_version>
        <firmware_version>2.3.34</firmware_version>
        <lan_ip></lan_ip>
        <wifi_ip>10.72.0.50</wifi_ip>
        <time>2019-09-16T20:07:35+02:00</time>
        <timezone>Europe/Amsterdam</timezone>
        <ssh_relay>disabled</ssh_relay>
        <project id='b44f8eec07da4eb18deca4f79ecadadf'>
            <name>Robbshop.nl</name>
            <description/>
            <is_default>false</is_default>
            <visible_in_production>false</visible_in_production>
            <deleted_date></deleted_date>
            <modified_date>2018-11-25T09:36:05.068+01:00</modified_date>
            <created_date>2018-10-30T19:02:01+01:00</created_date>
        </project>
        <gateway_environment id='1aa4c520fa504fb79ea970da11bd6213'>
            <savings_result_value/>
            <thermostat_model/>
            <country>NL</country>
            <electricity_consumption_tariff_structure/>
            <electricity_production_peak_tariff/>
            <central_heating_model/>
            <household_children>0</household_children>
            <thermostat_brand/>
            <electricity_production_off_peak_tariff/>
            <central_heating_installation_date/>
            <electricity_consumption_off_peak_tariff/>
            <gas_consumption_tariff/>
            <modified_date>2018-11-25T10:54:27.350+01:00</modified_date>
            <electricity_production_tariff_structure/>
            <housing_construction_period>unknown</housing_construction_period>
            <housing_type>apartment</housing_type>
            <currency>EUR</currency>
            <savings_result_unit/>
            <household_adults>0</household_adults>
            <central_heating_year_of_manufacture/>
            <deleted_date></deleted_date>
            <modified_date>2018-11-25T10:54:27.350+01:00</modified_date>
            <created_date>2019-09-15T13:47:11+02:00</created_date>
        </gateway_environment>
        <features>
            <remote_control id='746aefbab30a42bcaebe0591c6acda6e'>
                <activation_date>2018-09-04T10:24:20+02:00</activation_date>
                <validity_period/>
                <valid_to></valid_to>
                <valid_from></valid_from>
                <grace_period/>
                <deleted_date></deleted_date>
                <modified_date>2018-11-25T09:36:05.134+01:00</modified_date>
                <created_date>2018-09-04T10:24:20+02:00</created_date>
            </remote_control>
        </features>
    </gateway>
</domain_objects>

我想要做的是在结果 XML 文档中只获取第一个“网关”节点及其所有子节点。在此示例中,“domain_objects”节点下只有 1 个“网关”节点,但在另一个源文档中可能有多个网关节点。

此外,我希望将所有属性(来自第一个“网关”节点及其所有子节点)转换为元素。例如:

<remote_control id='746aefbab30a42bcaebe0591c6acda6e'>
....
</remote_control>

应该成为

<remote_control>
   <id>746aefbab30a42bcaebe0591c6acda6e</id>
....
</remote_control>

我目前拥有的 XSLT 几乎可以工作,但仅将“网关”节点本身的“id”属性转换为元素,但其子元素属性均未转换。

这就是我现在所拥有的:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="node() | @*">
        <!-- don't output anything now, but keep processing children -->
        <xsl:apply-templates />
    </xsl:template>

    <!-- modified identity transform -->
    <xsl:template match="/domain_objects/gateway[1]">
        <!-- <xsl:copy-of select="self::node()"/> -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="gateway"/>
        </xsl:copy>
    </xsl:template>

    <!-- attributes to elements -->
    <xsl:template match="@*" mode="gateway">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>        
    </xsl:template>

    <xsl:template match="*" mode="gateway">
        <xsl:copy-of select="self::node()"/>      
    </xsl:template>

    <!-- avoid mixed content -->
    <xsl:template match="text()[../@*]"/>
</xsl:stylesheet>

这导致:

<gateway>
   <id>8b33703f411e4fbba99884225a35d11c</id>
   <created_date>2018-10-30T12:51:41.555+01:00</created_date>
   ...
   <project id="b44f8eec07da4eb18deca4f79ecadadf">
      <name>Robbshop.nl</name>
      ....
   </project>
   ...
   <features>
      <remote_control id="746aefbab30a42bcaebe0591c6acda6e">
         <activation_date>2018-09-04T10:24:20+02:00</activation_date>
         <validity_period/>
         <valid_to/>
         <valid_from/>
         <grace_period/>
         <deleted_date/>
         <modified_date>2018-11-25T09:36:05.134+01:00</modified_date>
         <created_date>2018-09-04T10:24:20+02:00</created_date>
      </remote_control>
   </features>
   ...
</gateway>

如您所见,“remote_control”和“project”节点仍然具有它们的“id”属性。我的 XSLT 确实正确地将主“网关”节点属性“id”转换为元素,但它也应该为它的所有子节点执行此操作。我确定我忽略了一些非常简单的事情,但我真的很感激在正确方向上的一些帮助!

C队

您的主要问题是模板匹配“*”

<xsl:template match="*" mode="gateway">
    <xsl:copy-of select="self::node()"/>      
</xsl:template>

通过使用xsl:copy-ofcopy-of,您将原样复制元素,并且不会应用其他模板。您需要做的是允许使用其他模板。

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

话虽如此,您可以考虑重构您的 XSLT 样式表。与其想你需要复制什么,不如想想你不需要复制什么。

也试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

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

    <!-- modified identity transform -->
    <xsl:template match="domain_objects/gateway[position() > 1]" />

    <!-- avoid mixed content -->
    <xsl:template match="text()[../@*]"/>
</xsl:stylesheet>

编辑:为了回答你的评论,如果你不想domain_objects在你的 XML 中,但想要处理它的孩子,只需添加这个模板

<xsl:template match="domain_objects">
    <xsl:apply-templates />
</xsl:template>

或者,您也可以在此处包含仅选择第一个网关的逻辑:

<xsl:template match="domain_objects">
    <xsl:apply-templates select="gateway[1]" />
</xsl:template>

那么您将不需要删除其他gateway元素的其他模板,因为它们将不再被首先选中:

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章