【问题标题】:Flatten any XML using XSLT使用 XSLT 展平任何 XML
【发布时间】:2021-02-16 18:06:41
【问题描述】:

我正在尝试自动使用 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>

我尝试了很多东西,但我得到的更接近的是从this answer 中提取的。

<?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 cmets,一个例子并不构成规范。所以我想指出任何 cmets、处理指令、混合内容以及示例中没有的所有内容都应该被忽略。

【问题讨论】:

  • 一个示例不构成规范:您说“任何 XML”,但这意味着您需要指定属性、cmets、处理指令和混合内容会发生什么。
  • 感谢您的评论。你是对的。我编辑了我的问题并试图解释任何 cmets、处理指令等都应该被忽略。事实是我试图让问题简短。任何其他改进它的 cmet 都会受到欢迎。

标签: xml xslt xslt-2.0


【解决方案1】:

您可以使用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-3。但我很好奇,你的意思是你的第一个选项(使用 xslt-2)不适用于“巨大的文档”吗?
  • XSLT 1 和 2 处理完整 XML 输入文档的内存输入树,因此对于 GB 的 XML,您可能会遇到内存问题。 XSLT 3 中的流式处理是一种使用 XSLT 子集的方法,无需构建完整的输入树,而是一次性(仅转发)解析和处理。
  • 感谢您的解释和耐心等待。我并不期待特别大的文档,但如果可能的话,我还是会尝试使用 xslt-3。另外,我在我的问题中添加了一个警告,因此我将您的答案标记为不被接受。也许这应该是一个不同的问题?
  • @onzinsky,在答案似乎已经解决了原始问题之后,这绝不是一种使要求复杂化的好方法,如果没有的话,首先考虑尝试自己解决更复杂的情况工作将其作为一个新问题提出,显示您尝试了什么以及它是如何失败的。看来&lt;xsl:element name="{string-join((ancestor-or-self::*[position() ne last()]/name(), @type), '_')}"&gt; 可能就足够了。
猜你喜欢
  • 1970-01-01
  • 2018-08-25
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
  • 1970-01-01
  • 2015-11-29
相关资源
最近更新 更多