XSLTを積極的に使用してHTML(XHTMLではない)を生成する人は、有効なXML-XHTMLだけでなく、一般的には有効なHTMLを生成するためにXHTMLをサポートしないブラウザーでも生成する必要がある場合に遭遇する可能性があります同じこと。 これを行うために、XSLTで「ダーティハック」を使用しました。
この記事では、よりクリーンで美しい方法について説明しますが、残念ながら、この方法はあまり使用されません。
この方法は.Netインフラストラクチャに固有のものですが、他のプラットフォームにも同様のツールがおそらく存在します。
さて、順番に。
はじめに
XMLの情報表現は、HTMLドキュメントを記述するのに十分であり、さらに、XMLのフレームワークでは素晴らしいことは明らかです。 問題は、XMLのテキスト表現がHTMLの同じドキュメントの表現と異なる場合があることです。
問題の本質
HTMLからXMLをシリアル化する際の重要な違いは非常に簡単です。
- ドキュメントにxml宣言を含めることはできません 。
- 一部の要素には終了タグが必要です 。これは、標準のXMLシリアライザーが空のdivに対して誤って自己終了<div />タグを作成することを意味します。 HTMLパーサーは、divの終了タグを予期する必要があります。
- 一部の要素にはエンティティ参照を含めることができません。つまり、HTMLパーサーはスクリプトやスタイルなどの要素のエンティティ参照を処理しません。
さらに、コンテンツ自体に依存する制限があります(シリアライザーとは関係ありません)が、これらの制限を満たすことが重要です。
- 一部の要素はコンテンツを持つことができません 、つまり 空でなければなりません。 たとえば、リンク要素にはコンテンツが許可されていないため、リンクのコンテンツさえないが、独立した終了タグがある場合、これはHTMLパーサーエラーになります(もちろん無視されます)。
- 一部の要素には子またはコメントを含めることはできません ;これらはタイトルやテキストエリアなどの要素です。
- 文書の構造の一般的な制限、ここでは考慮しませんが、質問する心に任せます=)
メソッド自体
実際、XMLをシリアル化するために、環境はXmlWriterを使用します 。これは、XMLの適切なフォーマットに関するすべての作業を処理します。 このクラスは、何らかの形でXMLを記述する必要があるほとんどすべての操作で使用されます。 特に、XSL変換( XslCompiledTransform.Transform )では、このクラスのインスタンスが宛先として使用されます。
したがって、必要なのはXmlWriterを実装することだけです。これにより、HTMLルールに従ってXMLが正しくフォーマットされます。 だから、紹介-HtmlXmlWriter !
理論
HTML仕様、より具体的にはHTML5(現在はHTML5なし)を採用し、 5種類の要素が強調表示されていることがわかります。
- 無効な(空の)要素 -エリア、ベース、br、col、コマンド、埋め込み、hr、img、入力、keygen、リンク、メタ、パラメーター、ソース。
- 生テキスト(純粋なテキスト)要素 -スクリプト、スタイル。
- RCDATA要素(テキストのみ)-textarea 、title。
- 外部(外部)要素 -外部のHTML以外の要素、特にMathMLとSVGからの要素。ただし、このような要素はXHTML名前空間からではないものと見なします。
- 通常の要素 -他のすべてのHTML要素。
これで、HtmlXmlWriterが空の(void)要素にコンテンツを追加することを制御し、許可しないようにする必要があり、それらは常に自己終了型(<col />)になります。
純粋なテキスト(生のテキスト)にはテキストのみ(エンティティまたはコメントは不可)を含めることができますが、終了タグとして解釈できるシーケンスを含めることはできません(大文字と小文字に関係なく)。
RCDATAは子を持つことはできませんが、エンティティ参照を含むテキストのみを持つことができます。 それらのコメントも、不可能なようです。
外部(外部)要素は任意です-これはプレーンXMLです。 制限はありません。
通常の要素には必要なものを何でも含めることができますが、必要なのは終了タグだけです。
実装
まあ、実際には、ここでは実装を行いません。複雑ではなく、誰でも自分で実装できます。 私は自分でそれをしました。そして、おそらくそれを文書化するときに、彼らが涙を流して私に懇願するならば、私はそれをある種のコードリポジトリに投稿します。 ここでは、有用なメモのみを提供します(少し面倒です)。
HtmlXmlWriterはXmlWriterの子孫になります。 XmlWriterのサードパーティインスタンス(コンストラクターに渡す必要があります)を集約し、デフォルトで適切なメソッドを呼び出します。
HtmlXmlWriterは、現在の要素(最後の要素の名前と型)を追跡し、これをXmlWriter.WriteStartElement / WriteEndElementメソッドで定義する必要があります 。 また、属性(WriteStartAttribute / WriteEndAttribute)上にあるかどうかも追跡する必要があります。
要素(WriteEndElement / WriteFullEndElement)を閉じるときは、要素のタイプに応じてWriteEndElementまたはWriteFullEndElementを選択します。
XmlWriterは一部の文字をエスケープするため、最も難しい部分は生のテキスト要素です。 したがって、テキスト出力(WriteCharEntity、WriteString、WriteSurrogateCharEntity)をWriteRawで置き換える必要があります。 ただし、ここでは、テキストに終了タグがないように制御することを忘れてはなりません。
おわりに
そのようなクラスができたら、それをXSL変換(または他の場所)に簡単に渡すことができ、XHTMLから通常のHTMLを取得できるので、どんな愚かなHTMLパーサーでもこれを理解できます。