我对 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”转换为元素,但它也应该为它的所有子节点执行此操作。我确定我忽略了一些非常简单的事情,但我真的很感激在正确方向上的一些帮助!
您的主要问题是模板匹配“*”
<xsl:template match="*" mode="gateway">
<xsl:copy-of select="self::node()"/>
</xsl:template>
通过使用xsl:copy-of
copy-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] 删除。
我来说两句