0

我有一个文本文档,我想使用 XSLT 将其转换为 XML 以便于处理。源文件非常通用,例如:

[{c=1,d=2},{cc=11,dd=22}]%{f=4,g=5,h={i=6,j=[7,8]}}%

我想将其转换为 XML 文件,例如:

<document>
    <header>
        <item>
            <c>1</c>
            <d>2</d>
        </item>
        <item>
            <cc>11</c>
            <dd>22</d>
        </item>
    </header>
    <content>
        <f>4</f>
        <g>5</g>
        <h>
            <i>6</i>
            <j>
                <elt>7</elt>
                <elt>8</elt>
            </j>
        </h>
    </content>
</document>

所以本质上,“=”之前的字符串是标签名称,之后的所有内容都是内容(有嵌套),唯一的添加是文档、标题、内容和elt节点。原始文件可能会在单独的行中包含每个值和所有“}”,但这不能保证(我不知道这是否重要)

对于将文本转换为 XML 的类似情况,我找到了一些答案,但结果节点名称和嵌套级别总是事先知道的。直觉这个应该有比较简单的解决办法,可惜我只知道XSLT强大有用,不知道是谁写的……

提前感谢您的帮助,德科拉曼

4

3 回答 3

1

您基本上是在尝试为某些语法编写解析器。这是非常可行的,但它有助于准确地了解语法是什么,并且有助于了解一些关于如何编写递归下降解析器的知识。从您的示例来看,它看起来像一个递归语法,这意味着您不能纯粹使用正则表达式来做到这一点。

您可能想看看 Rex,Gunther Rademacher 的用于在 XQuery 或(最近)XSLT 中生成解析器的工具。它没有很好的文档记录,但它非常强大。

于 2013-05-01T19:58:10.497 回答
1

正如迈克尔建议的那样,这对于REx来说确实是一个不错的练习。该示例显示了与JSON的一些相似性,但为了演示,让我们猜测一个更简单的 REx 语法:

source     ::= item '%' item '%' eof
item       ::= '{' ( named-item ( ',' named-item )* )? '}'
             | '[' ( item ( ',' item )* )? ']'
             | element
named-item ::= name '=' item
<?TOKENS?>
name       ::= [a-z]+
element    ::= [0-9]+
eof        ::= $

将其放入名为 的文件中,并使用 REx 通过配置选项和或使用命令行source.ebnf从中生成 XSLT 编码的解析器。XSLTparse tree-xslt -tree

解析器包含一个名为的函数,该函数p:parse-source将输入作为字符串接受,并根据上述语法将其转换为具体的语法树。语法树包含每个非终结符或命名标记的元素,以及每个未命名标记的 TOKEN 元素。

然后必须将该语法树转换为目标结构。将生成的解析器从文件source.xslt导入到下面的 XSLT 中:

<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:p="source">

  <xsl:import href="source.xslt"/>
  <xsl:output indent="yes"/>
  <xsl:variable name="input" select="'[{c=1,d=2},{cc=11,dd=22}]%{f=4,g=5,h={i=6,j=[7,8]}}%'"/>

  <xsl:template match="/">
    <xsl:variable name="parse-tree" select="p:parse-source($input)"/>
    <xsl:choose>
      <xsl:when test="not($parse-tree/self::source)">
        <xsl:sequence select="$parse-tree"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="item">
          <xsl:apply-templates select="$parse-tree/item"/>
        </xsl:variable>
        <xsl:element name="document">
          <xsl:element name="header">
            <xsl:sequence select="$item/*[1]/node()"/>
          </xsl:element>
          <xsl:element name="content">
            <xsl:sequence select="$item/*[2]/node()"/>
          </xsl:element>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="item">
    <xsl:variable name="items">
      <xsl:apply-templates select="*[not(self::TOKEN)]"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="count($items/*) eq 1">
        <xsl:sequence select="$items"/>      
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="item">
          <xsl:sequence select="$items"/>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="named-item">
    <xsl:element name="{name}">
      <xsl:variable name="item">
        <xsl:apply-templates select="item"/>
      </xsl:variable>
      <xsl:sequence select="$item/*/node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="element">
    <xsl:element name="elt">
       <xsl:sequence select="node()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

在 XSLT 2.0 处理器(例如 Saxon)上运行上述程序将产生所需的结果。

于 2013-05-05T11:45:15.707 回答
0

在 XSLT 2.0 中有一个函数调用unparsed-text(),它将解析 HREF(或文件)并返回一个字符串。

然后,您可以使用一个或多个正则表达式指令或函数(例如tokenize()xsl:analyze-string)将字符串分解为序列并处理各个部分。

可以使用xsl:element指令在样式表中创建元素,如下所示:

<xsl:variable name="elementName" select="'f'"/>
<xsl:element name="$elementName">
  ..
</xsl:elelent>

显然,您将从字符串中获取元素名称,但希望您能看到使用的模式。

于 2013-05-01T14:21:02.373 回答