C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: MemoryStreamとXmlWriter


(過去ログ 45 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■23980 / inTopicNo.1)  MemoryStreamとXmlWriter
  
□投稿者/ kobachi (1回)-(2008/08/26(Tue) 02:54:09)
kobachi さんの Web サイト

分類:[C#] 

はじめまして。C#でしがないプログラムを開発しているkobachiと申します。
ちょっとした問題にぶつかりましたので、新参者ですが質問をさせてください。

C#のバージョンは、2.0です。

----------
■状況説明
----------

現在、C#で記述されたクラスをXmlSerializerによって変換し、
TCPで転送するプログラムを作成しております。
たとえば、

▼----------【クラスここから】----------▼
    public class Data{
        public string Test = "This is test.";
    }
▲----------【クラスここまで】----------▲

というようなクラスがあったとします。
これをTCPで送信することが目的なので、次のようなコードで直接byte[]に変換します。

▼----------【コードここから】----------▼
    Data data = new Data();
    byte[] result = new byte[0];
    using(MemoryStream stream = new MemoryStream()){
        using(XmlWriter writer = XmlWriter.Create(stream)){
            XmlSerializer serializer = new XmlSerializer(typeof(Data));
            serializer.Serialize(writer, data);
        }
        result = stream.ToArray();
    }
▲----------【コードここまで】----------▲

すると、先頭に余計な「?」が入ったXMLが出力されます。
これは整形式のXMLではないと思いますが、Deserialize()はエラーもなくちゃんと通ります。

▼----------【出力結果ここから】----------▼
?<?xml version="1.0" encoding="utf-8"?><Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Test>This is test.</Test></Data>
▲----------【出力結果ここまで】----------▲

XmlWriter.Create()にStringBuilderを渡した場合には、先頭に余計な「?」が入ることはありません。
XmlWriterSettingsを指定してみたりしましたが、特に変化はありません。


----------
■質問
----------

XmlWriter.Create()にMemoryStreamを渡してXmlSerializerによるSerializeを行ったとき、
先頭に「?」が挿入されたXMLが返ってくるのはバグでしょうか、それとも正常な動作なのでしょうか?
何か情報をご存じの方がいらっしゃいましたら、教えていただけないでしょうか。

引用返信 編集キー/
■23981 / inTopicNo.2)  Re[1]: MemoryStreamとXmlWriter
□投稿者/ やじゅ (574回)-(2008/08/26(Tue) 05:49:29)
やじゅ さんの Web サイト
2008/08/26(Tue) 05:51:47 編集(投稿者)

No23980 (kobachi さん) に返信
> ▼----------【出力結果ここから】----------▼
> ?<?xml version="1.0" encoding="utf-8"?><Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Test>This is test.</Test></Data>
> ▲----------【出力結果ここまで】----------▲
>
> XmlWriter.Create()にMemoryStreamを渡してXmlSerializerによるSerializeを行ったとき、
> 先頭に「?」が挿入されたXMLが返ってくるのはバグでしょうか、それとも正常な動作なのでしょうか?
> 何か情報をご存じの方がいらっしゃいましたら、教えていただけないでしょうか。

分からないつつも、怪しいとしたら、BOMですかね。
BOM (Byte Order Mark) バイト・オーダー・マーク
http://www.atmarkit.co.jp/aig/01xml/bom.html

BOM を削りつつ ↓は試してないので、実際にそうなのかは分かりません(^^;
byte[] preamble = encoding.GetPreamble();
int offset = preamble.Length;
result = encoding.GetString(stream.ToArray(), offset, (int)(ms.Length - offset));

参考:XML をインデントつきで出力する方法
http://www.smallbear.jp/tech/csharp/
引用返信 編集キー/
■23983 / inTopicNo.3)  Re[2]: MemoryStreamとXmlWriter
□投稿者/ なちゃ (164回)-(2008/08/26(Tue) 09:08:41)
UTF8Encodingだったか直に使えば、BOMなしも指定できるんじゃなかったかな?
ところで、?とは何で見たときに?なんでしょう?
多分BOMだとは思いますが。

引用返信 編集キー/
■23999 / inTopicNo.4)  Re[3]: MemoryStreamとXmlWriter
□投稿者/ kobachi (2回)-(2008/08/26(Tue) 14:17:03)
kobachi さんの Web サイト
やじゅ様 および なちゃ様
ご返答ありがとうございます。

質問時に掲載しましたコードは同じ状況が発生する最低限のコードでした。
実際にはEncodingを指定し、XmlWriterSettingsのEncodingと
GetString()に用いるEncodingを同じものにしていました。

>ところで、?とは何で見たときに?なんでしょう?

確認用出力は、Console.WriteLine()を使いました。
(が、問題はここにもありました!)

▼----------【Encodingを指定したつもりのコードここから】----------▼
Encoding utf8 = Encoding.UTF8;//= new UTF8Encoding();
using(MemoryStream stream = new MemoryStream()){
	XmlWriterSettings settings = new XmlWriterSettings();
	settings.Encoding = utf8;
	using(XmlWriter writer = XmlWriter.Create(stream, settings)){
		//中略
	}
	Console.WriteLine(utf8.GetString(stream.ToArray()));
}
▲----------【Encodingを指定したつもりのコードここまで】----------▲

BOMの可能性については上記のコードで解決したつもりでしたが・・・
実は違いました。


----------
■追試
----------

今まではVisual Studioだけでデバッグしていましたが、
文字エンコードが絡むことなので、
「信頼のおける」別のソフトを使って追試を行うことにしました。

秀丸エディタでUTF8 BOM付きのテキストを作成し、
バイナリエディタBzで作成したテキストを開いて、
バイナリ列をbyte[]に直接書き写したものをEncoding.UTF8でGetString()すると
先頭に「?」が挿入される現象が再現されます。
→やはり問題はBOMのようです

次に、日本語とアラビア語が混在したstringのインスタンスを持たせ、
EncodingをEncoding.Unicode(UTF16)に変更。
それを上記のコードでSerialize()→GetString()を試すと文字化けしました!
→XmlWriteSettings.Encodingが無視され、常にUTF-8でエンコードされているようです

さらに、Encoding.UTF8でも、System.Diagnostics.Debug.WriteLine()だと、
「?」が挿入されないことにも気づきました。
→あれ?

・・・という状況説明だけでは何も伝わらないと思うので、
同じ問題に遭遇した人(いるのか?)のためにまとめて起きたいと思います。


----------
■まとめ
----------

この問題の原因には、下記の2つの仕様が関わっているようです。

1. XmlWriterSetting.Encodingは、
  XMLのプリアンブルに含まれるencoding属性を書き換えるだけで、
  StreamにどのEncodingで書き込むかというのを制御しないという仕様。
  Encodingを指定するためには、StreamWriterのインスタンスを渡す必要がある。
    ▼----------【正常に動作するコードここから】----------▼
    Encoding utf8 = Encoding.UTF8;//= new UTF8Encoding();
    using(MemoryStream stream = new MemoryStream()){
        using(StreamWriter wrapper = new StreamWriter(stream, utf8)){
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = utf8;
            using(XmlWriter writer = XmlWriter.Create(wrapper, settings)){
                //中略
            }
            Console.WriteLine(utf8.GetString(stream.ToArray()));
        }
    }
    ▲----------【正常に動作するコードここまで】----------▲

2. Console.WriteLine()は日本語版WindowsではCP932への変換を経てVisual Studioに伝わるため、
  Unicode文字列(BOMも)をConsole.WriteLine()で出力すると正常に表示されないという仕様。
  参考:http://oshiete1.goo.ne.jp/qa684358.html

ということが分かりました。

お二方のご指摘通り問題はBOMでした。
Encodingが絡む場合はかなり注意してコードを組まないといけないんですね・・・

どうもありがとうございました。

解決済み
引用返信 編集キー/
■24017 / inTopicNo.5)  Re[4]: MemoryStreamとXmlWriter
□投稿者/ Jitta on the way (159回)-(2008/08/26(Tue) 17:37:08)
No23999 (kobachi さん) に返信

>   参考:http://oshiete1.goo.ne.jp/qa684358.html

orz orz orz
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -