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 が強力で便利であることしか知りませんが、誰がそれを書くべきかはわかりません...

助けてくれてありがとう、DeColaman

4

3 に答える 3

1

Michael が示唆したように、これは確かに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        ::= $

それを という名前のファイルに入れ、source.ebnfオプションXSLTおよびを構成するparse treeか、コマンド ライン を使用して、REx を使用してそこから XSLT コードのパーサーを生成します-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 に答える
1

基本的に、文法のパーサーを作成しようとしています。これは非常に実行可能ですが、文法が何であるかを正確に知るのに役立ち、再帰降下パーサーの書き方について少し知るのに役立ちます。あなたのサンプルからは、再帰的な文法のように見えます。つまり、正規表現だけを使用して実行することはできません。

XQuery または (最近) XSLT でパーサーを生成するための Gunther Rademacher のツールである Rex を参照してください。十分に文書化されていませんが、非常に強力です。

于 2013-05-01T19:58:10.497 に答える