36

XSLT を使用して、XML ファイルを dokuwiki で使用されるマークアップに変換しようとしています。これは実際にはある程度機能しますが、XSL ファイルのインデントが結果に挿入されています。現時点では、私には 2 つの選択肢があります。この XSLT を完全に放棄するか、XML から DokuWiki マークアップに変換する別の方法を見つけるか、XSL ファイルから空白の約 95% を削除して、ほとんど判読不能にしてメンテナンスの悪夢にします。

すべての空白を最終文書に渡すことなく、XSL ファイルにインデントを保持する方法はありますか?

背景: autodoc ツールを静的な HTML ページから Dokuwiki に移行しているので、サーバー チームによって開発された API は、アプリ チームが文書化されていないコードに出くわしたときはいつでも、アプリケーション チームによってさらに文書化できます。ロジックは、autodoc ツール用に各ページのセクションを確保し、このブロックの外側のどこにでもコメントを許可することです。XML から XHTML に変換する XSL ファイルが既にあるため、XSLT を使用しています。また、独自のソリューションをゼロから作成するよりも、XSL を書き直す方が高速であると想定しています。

編集:ああ、そうです、愚かな私はインデント属性を無視しました。(その他のバックグラウンド ノート: 私は XSLT を初めて使用します。) 一方で、まだ改行を処理する必要があります。Dokuwiki はパイプを使用して表の列を区別します。つまり、表の行のすべてのデータは 1 つの行にある必要があります。改行が出力されるのを抑制する方法はありますか (たまに)、テーブル セルごとにかなり複雑なロジックをやや読みやすい方法で実行できますか?

4

4 に答える 4

77

XSLT 変換の結果に不要な空白が含まれる理由は 3 つあります。

  1. ソース文書のノード間から来る空白
  2. ソース ドキュメントのノード内にある空白
  3. スタイルシートからの空白

空白がどこから来ているのかを判断するのは難しいため、3 つすべてについて説明します。そのため、いくつかの戦略を使用する必要があるかもしれません。

ソース ドキュメントのノード間の空白に対処するには、 を使用<xsl:strip-space>して 2 つのノード間に表示される空白を取り除き、 を使用<xsl:preserve-space>して混合コンテンツ内に表示される可能性のある重要な空白を保持する必要があります。たとえば、ソース ドキュメントが次のようになっているとします。

<ul>
  <li>This is an <strong>important</strong> <em>point</em></li>
</ul>

その場合、重要ではないとと の間の<ul>と の間の空白を無視する必要がありますが、重要な 要素と要素の間の空白を保持します(そうしないと、「これ**重要** です。 *点*")。これを行うには<li></li></ul><strong><em>

<xsl:strip-space elements="*" />
<xsl:preserve-space elements="li" />

elementson 属性は基本的に、<xsl:preserve-space>混合コンテンツを持つ文書内のすべての要素をリストする必要があります。

余談ですが、 を使用<xsl:strip-space>すると、メモリ内のソース ツリーのサイズも縮小され、スタイルシートがより効率的になるため、この種の空白の問題がなくても実行する価値があります。

ソース ドキュメントのノード内に表示される空白に対処するには、 を使用する必要がありますnormalize-space()。たとえば、次の場合:

<dt>
  a definition
</dt>

そして、<dt>要素が何かをしたい要素を保持していないことを確認できます。次に、次のことができます。

<xsl:template match="dt">
  ...
  <xsl:value-of select="normalize-space(.)" />
  ...
</xsl:template>

先頭と末尾の空白は<dt>要素の値から取り除かれ、文字列が取得されます"a definition"

おそらくあなたが経験しているものであるスタイルシートからの空白に対処するには、次のようなテンプレート内にテキストがある場合です。

<xsl:template match="name">
  Name:
  <xsl:value-of select="." />
</xsl:template>

XSLT スタイルシートは、処理するソース ドキュメントと同じ方法で解析されるため、上記の XSLT は、最初の子がテキスト ノードで、2 番目の子が属性を持つ要素である属性を持つ要素<xsl:template>を保持するツリーとして解釈されます。テキスト ノードには、先頭と末尾の空白 (改行を含む) があります。これはスタイルシートのリテラル テキストであるため、先頭と末尾のすべての空白とともに、文字どおり結果にコピーされます。match<xsl:value-of>select

ただし、XSLT スタイルシートの一部の空白、つまりノード間の空白は自動的に削除されます。<xsl:value-of>と の終わりの間に改行があるため、結果に改行はありません<xsl:template>

結果に必要なテキストのみを取得するには、次の<xsl:text>ように要素を使用します。

<xsl:template match="name">
  <xsl:text>Name: </xsl:text>
  <xsl:value-of select="." />
</xsl:template>

XSLT プロセッサは、ノード間に表示される改行とインデントを無視し、<xsl:text>要素内のテキストのみを出力します。

于 2008-10-08T21:46:07.470 に答える
4

出力タグで indent="no" を使用していますか?

<xsl:output method="text" indent="no" />

また、xsl:value-of を使用している場合は、disable-output-escaping="yes" を使用して空白の問題を解決できます。

于 2008-10-08T19:26:46.300 に答える
3

@JeniTの答えは素晴らしいです。空白を管理するためのトリックを指摘したいだけです。それが最善の方法(または良い方法でさえある)であるとは確信していませんが、今のところうまくいきます。

(「s」はスペース、「e」は空白、「n」は改行を表します。)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:transform [
  <!ENTITY s "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
  <!ENTITY s2 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>  </xsl:text>" >
  <!ENTITY s4 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>    </xsl:text>" >
  <!ENTITY s6 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>      </xsl:text>" >
  <!ENTITY e "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'></xsl:text>" >
  <!ENTITY n "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
</xsl:text>" >
]>

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
  &e;Flush left, despite the indentation.&n;
  &e;  This line will be output indented two spaces.&n;

      <!-- the blank lines above/below won't be output -->

  <xsl:for-each select="//foo">
    &e;  Starts with two blanks: <xsl:value-of select="@bar"/>.&n;
    &e;  <xsl:value-of select="@baz"/> The 'e' trick won't work here.&n;
    &s2;<xsl:value-of select="@baz"/> Use s2 instead.&n;
    &s2;    <xsl:value-of select="@abc"/>    <xsl:value-of select="@xyz"/>&n;
    &s2;    <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n;
  </xsl:for-each>
</xsl:template>
</xsl:transform>

に適用されます:

<?xml version="1.0" encoding="UTF-8"?>
<foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo>

出力:

Flush left, despite the indentation.
  This line will be output indented two spaces.
  Starts with two blanks: bar.
baz The 'e' trick won't work here.
  baz Use s2 instead.
  abcxyz
  abc xyz

'e' トリックは、少なくとも 1 つの非空白文字を含むテキスト ノードの前で機能します。これは、次のように展開されるためです。

<xsl:template match="/">
  <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text>
</xsl:text>

空白を削除するための規則では、空白のみのテキスト ノードが削除されるため、 <xsl:template> と <xsl:text> の間の改行とインデントが削除されます (適切)。ルールでは、少なくとも 1 つの空白文字を含むテキスト ノードが保持されると規定されているため、暗黙的なテキスト ノードを含む暗黙的なテキスト ノード" This line will be output indented two spaces."は先頭の空白を保持します (ただし、これはストリップ/保持/正規化の設定にも依存すると思います)。それで;" 行末に改行が挿入されますが、後続の空白は 2 つのノードの間に表示されるため、無視されることも保証されます。

問題は、<xsl:value-of> で始まるインデントされた行を出力したい場合です。その場合、「&e;」インデントの空白は空白以外の文字に「接続」されていないため、役に立ちません。そのような場合には、「&s2;」を使用します。必要なインデントの量に応じて、または「&s4;」。

確かにこれは醜いハックですが、少なくとも XSLT を散らかす冗長な「<xsl:text>」タグはありません。少なくとも、読みやすいように XSLT 自体をインデントすることはできます。設計されていないもの (テキスト処理) のために XSLT を悪用しているように感じますが、これが私ができる最善のことです。


編集: コメントに応じて、これは「マクロ」なしでどのように見えるかです:

<xsl:template match="/">
  <xsl:text>Flush left, despite the indentation.</xsl:text>
  <xsl:text>  This line will be output indented two spaces.</xsl:text>
  <xsl:for-each select="//foo">
    <xsl:text>  Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text>
</xsl:text>
    <xsl:text>    </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text>
</xsl:text>
  </xsl:for-each>
</xsl:template>

これにより、意図した出力インデントがわかりにくくなり</xsl:text>、XSL ファイルの列 1 に終了タグを表示する必要があるため、XSL 自体のインデントが台無しになると思います (そうしないと、出力ファイルに不要な空白ができてしまいます)。

于 2011-01-16T05:01:57.063 に答える
0

新しい行に関する編集に関しては、このテンプレートを使用して、ある文字列を別の文字列に再帰的に置き換えることができ、改行に使用できます。

<xsl:template name="replace.string.section">
  <xsl:param name="in.string"/>
  <xsl:param name="in.characters"/>
  <xsl:param name="out.characters"/>
  <xsl:choose>
    <xsl:when test="contains($in.string,$in.characters)">
      <xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/>
      <xsl:call-template name="replace.string.section">
        <xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/>
        <xsl:with-param name="in.characters" select="$in.characters"/>
        <xsl:with-param name="out.characters" select="$out.characters"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$in.string"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template> 

次のように呼び出します (この例では、$some.string 変数の改行をスペースに置き換えます)。

    <xsl:call-template name="replace.string.section">
        <xsl:with-param name="in.string" select="$some.string"/>
        <xsl:with-param name="in.characters" select="'&#xA;'"/>
        <xsl:with-param name="out.characters" select="' '"/>
    </xsl:call-template>
于 2008-10-08T21:07:49.547 に答える