XMLをプレーンテキストに変換する-XSLTの空白を無視/処理する方法

StackOverflow https://stackoverflow.com/questions/184431

  •  06-07-2019
  •  | 
  •  

質問

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

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

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

編集:ああ、そうだ、馬鹿だ、インデント属性を無視した。 (他の背景ノート:私はXSLTが初めてです。)一方、私はまだ改行に対処する必要があります。 Dokuwikiはパイプを使用してテーブル列を区別します。つまり、テーブル行のすべてのデータは1行である必要があります。改行が出力されるのを抑制する方法はありますか(たまに)

役に立ちましたか?

解決

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>と<=の間の空白は保持します。 >要素、重要な (そうでない場合は<!> quot;これは**重要な***ポイント* <!> quot;です)。これを行うには

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

<em>elements属性には、基本的に、コンテンツが混在しているドキュメント内のすべての要素がリストされます。

  

さておき:normalize-space()を使用すると、メモリ内のソースツリーのサイズが小さくなり、スタイルシートがより効率的になるため、この種の空白の問題がない場合でも実行する価値があります。

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

<dt>
  a definition
</dt>

そして"a definition"要素があなたが何かをしたい要素を保持しないことを確実にすることができます、そしてあなたはそうすることができます:

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

先頭および末尾の空白は<xsl:template>要素の値から削除され、文字列matchが取得されます。

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

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

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

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

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

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

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

他のヒント

indent = <!> quot; no <!> quot;を使用していますか?出力タグに入れますか?

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

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

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

(<!> quot; s <!> quot;スペースの場合、<!> quot; e <!> quot;空の場合、<!> quot; n <!> quot;改行の場合。)

<?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>

空白を削除する規則では、空白のみのテキストノードが削除されると規定されているため、 <!> lt; xsl:template <!> gt;の間の改行とインデント。および<!> lt; xsl:text <!> gt;剥がされる(良い)。ルールでは、少なくとも1つの空白文字を含むテキストノードが保持されると規定されているため、" This line will be output indented two spaces."を含む暗黙のテキストノードは先頭の空白を保持します(ただし、これはstrip / preserve / normalizeの設定にも依存します)。 <!> quot; <!> amp; n; <!> quot;行の最後に改行が挿入されますが、2つのノード間に表示されるため、後続の空白は無視されます。

問題は、<!> lt; xsl:value-of <!> gt;で始まるインデントされた行を出力したい場合です。その場合、<!> quot; <!> amp; e; <!> quot;インデントの空白は<!> quot; attached <!> quotではないため、助けにはなりません。任意の非空白文字に。したがって、これらのケースでは、<!> quot; <!> amp; s2; <!> quot;を使用します。または<!> quot; <!> amp; s4; <!> quot ;、必要なインデントの量によって異なります。

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


編集: コメントへの応答では、これは<!> quot; macros <!> quot;:

なしの外観です。
<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ファイルの列1に</xsl:text>終了タグを表示する必要があるため、XSL自体のインデントを台無しにします(そうしないと、不要な空白が発生します)出力ファイル)。

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

<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>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top