【问题标题】:Splitting an element in XML based on its text using XSLT使用 XSLT 根据其文本拆分 XML 中的元素
【发布时间】:2013-01-25 18:38:31
【问题描述】:

我正在使用 XSLT 将一个 XML 文件转换为另一个 XML 文件。 XML 文件有这样的元素:

<Course>
      <ID>1001</ID>
      <Seats>10</Seats>
      <Description>Department: CS , Faculty: XYZ</Description>
</Course>

现在在 XSLT 中有什么方法可以让我生成如下所示的新 XML 文件:

<Course>
      <ID>1001</ID>
      <Seats>10</Seats>
      <Department> CS </Department> 
      <Faculty> XYZ</Faculty>
</Course>

也就是说,我想将 Description 元素分成两个不同的元素 Department 和 Faculty,它们基​​本上是用逗号分隔的内容。我使用 XMLspy 编写了我的 XSLT。

提前谢谢你。

【问题讨论】:

    标签: xml xslt-2.0


    【解决方案1】:

    这是一种可能的 XSLT2 解决方案,它基于特定于描述元素的模板中的身份转换和标记化字符串函数。

    一般的想法是首先将描述字符串拆分为“,”,然后将每个结果子字符串拆分为“:”,只选择最后一部分。

    <?xml version="1.0"?>
    
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="Description">
            <xsl:variable name="tokens" select="fn:tokenize(text(),',')"/>
            <xsl:element name="Department"><xsl:value-of select="fn:normalize-space(fn:tokenize($tokens[1],':')[2])"/></xsl:element>
            <xsl:element name="Faculty"><xsl:value-of select="fn:normalize-space(fn:tokenize($tokens[2],':')[2])"/></xsl:element>
        </xsl:template>
    
    </xsl:stylesheet>
    

    normalize-space 函数作为最后一步调用,以去除前导/尾随空格;如果这不是必需的,请忽略这一点。

    警告购买者:这里的假设是描述文本的格式是固定的(即部门和学院总是以相同的顺序出现。)此外,假设描述元素中既没有“:”也没有“,”文本。

    上面的转换产生了预期的结果:

    <?xml version="1.0" encoding="UTF-8"?><Course>
          <ID>1001</ID>
          <Seats>10</Seats>
          <Department>CS</Department>
          <Faculty>XYZ</Faculty>
    </Course>
    

    请注意,在纯文本运行内部包含结构化信息并不能充分利用 XML,这完全是关于结构的,但我猜这种格式不是你可以控制的。

    基于 cmets 的更新:

    下面列出了基于regular expression 匹配的更强大的替代解决方案。在这种情况下,只有与 Department、Faculty 模式匹配的 Description 元素被重写;否则原始Description元素会被传递:

    <?xml version="1.0"?>
    
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="Description">
            <xsl:analyze-string select="." regex="\s*Department:\s*(.+)\s*,\s*Faculty:\s*(.+)\s*">
                <xsl:matching-substring>
                    <xsl:element name="Department"><xsl:value-of select="regex-group(1)"/></xsl:element>
                    <xsl:element name="Faculty"><xsl:value-of select="regex-group(2)"/></xsl:element>
                </xsl:matching-substring>
                <xsl:non-matching-substring>
                    <xsl:element name="Description"><xsl:copy/></xsl:element>
                </xsl:non-matching-substring>
            </xsl:analyze-string>
        </xsl:template>
    
    </xsl:stylesheet>
    

    这里的关键思想是使用xsl:analyze-string 通过XSLT regular expression 测试是否找到了预期的模式,并在这种情况下捕获相应的值。如果未找到匹配项,则复制 Description 元素的原始内容。

    注意:将其与根元素集成留给读者作为练习(因为 OP 示例没有显示 Course 元素适合的位置)。

    【讨论】:

    • 非常感谢。我尝试了您建议的某些修改,但是当我尝试运行它时,它给了我错误 XSLT 2.0 调试错误:意外元素 XSLT 2.0 调试错误:尝试启动调试器时发生错误!我是否缺少在我的代码中包含一些命名空间或任何其他此类引用??
    • 感谢您的帮助。我发现了错误。
    • 很高兴能帮上忙!如果此答案解决了您的问题,请考虑勾选已接受标记。
    • 是的,我可以做到。再多两个查询: > 如果我的 XML 文件中有两个或更多课程元素,并且我想在仅发生上述模式的匹配时对描述元素应用上述转换,否则按原样获取描述元素。那我该怎么做。我尝试使用 xsl:matching-substring 和 xsl:non-matching-substring 但它没有成功。 2> 我如何理解 Regex for XML 以备将来使用。在此先感谢
    • 发布了基于正则表达式匹配的替代解决方案。如果您觉得这很有用,请考虑支持并标记为接受。 ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 2022-10-20
    • 1970-01-01
    相关资源
    最近更新 更多