使用 XSLT 展平任何 XML
Flatten any XML using XSLT
我正在尝试使用 XSLT 自动 展平 任何 XML 文件。它可以实现吗?我猜是的,但我找不到办法。
示例输入
<person>
<name>
<first>John</first>
<last>Doe</last>
</name>
<data>
<address>
<street>Main</street>
<city>Los Angeles</city>
</address>
</data>
</person>
预期输出
<person>
<name_first>John</name_first>
<name_last>Doe</name_last>
<data_address_street>Main</data_address_street>
<data_address_city>Los Angeles</data_address_city>
</person>
我已经尝试了很多东西,但我得到的更近的是从 中提取的。
<?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="xs" version="2.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="/*/*">
<xsl:for-each select="*">
<xsl:element name="{concat(name(..),'_',name())}">
<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
正如@Michael Kay 评论的那样,一个示例不构成规范。所以我想指出任何注释、处理指令、混合内容以及示例中没有的所有内容都应该被忽略。
你可以用 string-join
:
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="descendant::*[not(*)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{string-join(ancestor-or-self::*[position() ne last()]/name(), '_')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
对于大型文档和 XSLT 3 以及流式传输(例如 Saxon EE),您可以做到
<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:mode streamable="yes"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="descendant::text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:element name="{string-join(ancestor::*[position() lt last()]/name(), '_')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
我正在尝试使用 XSLT 自动 展平 任何 XML 文件。它可以实现吗?我猜是的,但我找不到办法。
示例输入
<person>
<name>
<first>John</first>
<last>Doe</last>
</name>
<data>
<address>
<street>Main</street>
<city>Los Angeles</city>
</address>
</data>
</person>
预期输出
<person>
<name_first>John</name_first>
<name_last>Doe</name_last>
<data_address_street>Main</data_address_street>
<data_address_city>Los Angeles</data_address_city>
</person>
我已经尝试了很多东西,但我得到的更近的是从
<?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="xs" version="2.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="/*/*">
<xsl:for-each select="*">
<xsl:element name="{concat(name(..),'_',name())}">
<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
正如@Michael Kay 评论的那样,一个示例不构成规范。所以我想指出任何注释、处理指令、混合内容以及示例中没有的所有内容都应该被忽略。
你可以用 string-join
:
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="descendant::*[not(*)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{string-join(ancestor-or-self::*[position() ne last()]/name(), '_')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
对于大型文档和 XSLT 3 以及流式传输(例如 Saxon EE),您可以做到
<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:mode streamable="yes"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="descendant::text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:element name="{string-join(ancestor::*[position() lt last()]/name(), '_')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>