XmlWriter で StringWriter を使うと Encoding が「UTF-16」になってしまうの対処方法

ExDoc 絡みで、今更ながら XmlWriter クラスを使って xml 形式のデータを出力しています。
そこで、XmlWriter クラスのエンコードなのですが、デフォルトでは「UTF-8」なんだけど、何故か StringWriter を使って文字列に吐き出させると「<?xml version=”1.0″ encoding=”utf-16″?>
」として吐き出されてしまう、問題があります。

ファイル自身は「utf-8」なのに、xml ヘッダが「utf-16」では、困るわけですね。

ややこしいのでコードの抜粋を示すと、

/// <summary>
/// ドキュメントから文字列に変換
/// </summary>
/// <returns></returns>
public string SaveXML()
{
	StringWriter sw = new StringWriter();
	XmlWriterSettings setting = new XmlWriterSettings();
	setting.Indent = true;
	XmlWriter writer = XmlWriter.Create(sw, setting);

	writer.WriteStartDocument();
	SaveXML(writer, this.DocumentElement);
	writer.WriteEndDocument();
	writer.Close();
	string xml = sw.ToString();
	sw.Close();
	return xml;
}

こんな風に、XmlWriter を使って出力する先を、StringWriter にしています。MSDN のドキュメントを見ると、

XmlWriterSettings.Encoding プロパティ (System.Xml)
http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=JA-JP&k=k(SYSTEM.XML.XMLWRITERSETTINGS.ENCODING);k(SOLUTIONITEMSPROJECT);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true

なところで、XmlWriter は渡されたクラスのエンコードを使うので、XmlWriter 自身のエンコードは無視される、という書き方がされています。実際、StringWriter は内部コードなので、Unicode を使っているので「UTF-16」となるのは動きとしては正しいのですが、この StringWriter オブジェクトを、ファイルに書き込もうとするとちょっと困ったことになるのです。

/// <summary>
/// ドキュメントからファイルを作成する
/// </summary>
/// <param name="path"></param>
public void Save(string path)
{
	StreamWriter sr = new StreamWriter(new FileStream(path, FileMode.Create));
	string xml = SaveXML();
	sr.Write(xml);
	sr.Close();
}

こんな風にエンコードを指定しないと、ファイルは「UTF-8」で書かれてしまうのです。なので、

  • ファイルのコードは「UTF-8」なのだが、
  • XML のヘッダには「UTF-16」と書いてある。

という不整合のファイルができてしまいます。仕方がないので、どちらかのエンコードを替えます。ファイルのエンコードを「UTF-16」に揃えてもよいのですが、どちらかというと XML ファイル自体を「UTF-8」に揃えておきたい訳です。
そうなると、先の StringWriter の Encoding プロパティの値が邪魔で、なんともできないなぁ、と思っていたのですが。

下記のように、StringWriter クラスを継承して、Encoding プロパティでは、常に「UTF-8」を返すようにすれば良いのでした。なるほど、確かにこれで XML のヘッダ部分が「UTF-8」になります。

private class StringWriterUTF8 : StringWriter
{
	public override System.Text.Encoding Encoding
	{
		get { return System.Text.Encoding.UTF8; }
	}
}

/// <summary>
/// ドキュメントから文字列に変換
/// </summary>
/// <returns></returns>
public string SaveXML()
{
	StringWriter sw = new StringWriterUTF8();
	XmlWriterSettings setting = new XmlWriterSettings();
	setting.Indent = true;
	XmlWriter writer = XmlWriter.Create(sw, setting);

	writer.WriteStartDocument();
	SaveXML(writer, this.DocumentElement);
	writer.WriteEndDocument();
	writer.Close();
	string xml = sw.ToString();
	sw.Close();
	return xml;
}

こんな風に、内部クラスとして StringWriter を継承したクラスを作ると、変更が局所化されて便利です。

カテゴリー: C#, EXDoc パーマリンク

XmlWriter で StringWriter を使うと Encoding が「UTF-16」になってしまうの対処方法 への1件のコメント

  1. masuda のコメント:

    気づいたのだけど、UTF-8 で保存する時に xml ヘッダを書き換えるほうが簡単ですね。
    実際ファイル書き込み時に xml ヘッダを付けるほうが理に適っているし。

コメントは停止中です。