C#でXMLのスタイルシートの指定をする

XSLTを利用したXMLファイルのメンテナンスをするためにC#でツールを作ると便利ですよね。でもC#のXML serializerには、スタイルシートを指定する方法が見つかりませんでした。ということで別の方法がありますので、メモっておきます。

目次

  1. XMLのスタイルシートの指定とは
  2. スタイルシートを指定してXMLを書き出す
  3. XMLファイルのスタイルシートの指定を読み取る
  4. 試してみた
    1. シナリオ
    2. コード
    3. 動かしてみた
  5. まとめ

XMLのスタイルシートの指定とは

XSLTというXML文書を見た目わかりやすくするように変換する仕組みがあります。変換の手順を示したファイルをXMLファイルとは別のファイルに保存しておいて、XMLファイルの中にそのXSLT用のファイルを指定しておきます。そうすると、ウェブブラウザのようなXMLを解釈できるアプリでXMLファイルを開いたときに、自動的にそのXSLT用のファイルを読み取って、その指示に従ってXML文書を整形して表示されます。

XMLファイルの中に下記のように記述します。で、test.xslの中に整形の指示を書くわけです。

<?xml-stylesheet type="text/xsl" href="test.xsl"?>

HTMLでもスタイルシート(CSS)をHTML文書内に指定しますよね。それと同じような感じです。

スタイルシートを指定してXMLを書き出す

C#でオブジェクトをXMLに変換する方法といえば、 XmlSerializerクラス です。XMLの構造に合わせたオブジェクトを作っておけば、XmlSerializerがうまくオブジェクトからXMLに変換したり(シリアル化)XMLからオブジェクトに変換したり(逆シリアル化)してくれるので楽です。

XmlSerializerで何もせずにシリアル化すると自動的にXML名前空間の指定がされるのですが、これを書き出したくなければ XmlSerializerNamespacesクラス を利用して名前空間の出力を止められます。

スタイルシートの指示の出力については、MSDNのXmlSerializerのあたりを読んでみましたが見つかりませんでした。代わりに、 XmlWriterクラスWriteProcessingInstruction()メソッド を使用します。

XmlWriter writer = XmlWriter.Create(outputStream,outputSetting);
writer.WriteProcessingInstruction("xml-stylesheet", @"type=""text/xsl"" href=""test.xsl""");

上記のようにXmlWriterオブジェクトを作って、そのオブジェクトにWriteProcessingInstructionでスタイルシートのファイルを指定します。このXmlWriterオブジェクトをXmlSerializerのSerialize()メソッドの第1引数に指定して、XMLファイルの書き込みをします。そうすると、スタイルシートの指定がXMLファイルに書き込まれます。上記の場合は、XMLファイル内に下記が書き込まれます。

<?xml-stylesheet type="text/xsl" href="test.xsl"?>

XMLファイルのスタイルシートの指定を読み取る

「XMLファイルを読み込んで、中身を更新して、結果を出力する。」というアプリを作る場合、XMLファイルを上書きするのが簡単ですね。そうすると、スタイルシートの指定があるXMLファイルの場合は、スタイルシートの指定部を読み込まないといけません。

スタイルシートの指定の書き出しはXmlWriterでするということは、きっとXmlReaderにそういう機能があるに違いないと思ったのですが、MSDNを見ていてもわかりませんでした。

ということで、XMLファイルはテキストファイルなんだから正規表現で検索すれば良いやと、正規表現に頼ってみました。きっともっとスマートな方法があるんでしょうねえ。

正規表現での検索は下記のようにしてみました。

Match regexResult = Regex.Match("XML文書全文を入れた文字列変数", @"<\?\s*xml-stylesheet\s+(.+?)\s*\?>",RegexOptions.Singleline);
styleString = regexResult.Groups[1].Value;

そして、XmlWriterオブジェクトには下記のように指示します。

writer.WriteProcessingInstruction("xml-stylesheet", styleString);

試してみた

シナリオ

下記のXMLファイルを読み込んで、読み取り結果を出力します。コンソールアプリです。

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<member>
  <person>睦美ほむら</person>
</member>

出力したら、person要素を「ほむほむ」にしてXMLファイルを上書きします。

コード

コードは下記のようにしました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text.RegularExpressions;

namespace xml_trial2
{
    class Program
    {
        static void Main(string[] args)
        {
            // 読み込み
            FileStream inputStream = new FileStream(@"export.xml", FileMode.Open); // ファイルストリームのインスタンスを作る。
            StreamReader reader = new StreamReader(inputStream); // StreamReaderのインスタンスを作る。
            string styleString = reader.ReadToEnd(); // 文字列変数に読み込む。
            Match regexResult = Regex.Match(styleString, @"&lt;\?\s*xml-stylesheet\s+(.+?)\s*\?&gt;",RegexOptions.Singleline); // 文字列からスタイルシート指定部分を検索する。
            styleString = regexResult.Groups[1].Value; // 検索結果から文字列を取り出す。

            inputStream.Position = 0; // ストリームのポジションを戻す。
            XmlSerializer serializer = new XmlSerializer(typeof(xml_trial2.Member)); // シリアライザーのインスタンスを作る。
            xml_trial2.Member model = (xml_trial2.Member)serializer.Deserialize(inputStream); // 逆シリアライズしてオブジェクトに格納する。
            inputStream.Close(); // ストリームを閉じる。

            Console.WriteLine(String.Format("Person={0}", model.person)); // 読み込んだデータの表示

            //書き出し
            xml_trial2.Member trial_member = new xml_trial2.Member(); // 書き出し用データのインスタンスを作る。
            trial_member.person = "ほむほむ"; // 書き出し用データの内容を設定する。

            FileStream outputStream = new FileStream("export.xml", FileMode.Create); // ファイルストリームのインスタンスを作る。
            XmlWriterSettings outputSetting = new XmlWriterSettings(); // XML Writerの設定用のインスタンスを作る。
            outputSetting.Encoding = Encoding.UTF8; // エンコード
            outputSetting.NewLineChars = "\n"; // 改行コード
            outputSetting.Indent = true; // インデントの要否
            XmlWriter writer = XmlWriter.Create(outputStream,outputSetting); // XML Writerのインスタンスを作る。
            writer.WriteProcessingInstruction("xml-stylesheet", styleString); // スタイルシートの指定をする。
            // writer.WriteProcessingInstruction("xml-stylesheet", @"type=""text/xsl"" href=""test.xsl"""); // スタイルシートの指定を直接する場合はこちら。

            XmlSerializerNamespaces outputNameSpace = new XmlSerializerNamespaces();// XML名前空間のインスタンスを作る。
            outputNameSpace.Add(String.Empty, String.Empty); // XML名前空間の値を設定する。

            XmlSerializer outputSerializer = new XmlSerializer(typeof(xml_trial2.Member)); // シリアライザーのインスタンスを作る。
            outputSerializer.Serialize(writer, trial_member, outputNameSpace); // オブジェクトをシリアライズして書き込む。

            writer.Flush();
            writer.Close(); // ストリームを閉じる。
        }
    }
}

usingにいくつか追加します。

  • System.IO → ストリームを使った入出力用です。

  • System.Xml → XmlWriter用です。

  • System.Xml.Serialization → XMLのシリアル化/逆シリアル化用です。

  • System.Text.RegularExpressions → 正規表現での検索用です。

あとはコメントの通りです。

動かしてみた

実行結果は下記の通りです。

実行結果

出力されたXMLファイルの中身は下記になりました。ちゃんとスタイルシートの宣言がありますね。

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<member>
  <person>ほむほむ</person>
</member>

まとめ

この方法ではXMLファイルが大きくなればなるほど処理が大変になりますよね、きっと。自家製ツールならではの富豪プログラミングです。詳しい人はLINQとか使うのでしょうか。

公開日

広告