LINQ to XMLのNamespaceと書き出し時のEncodingについて
- C# FotolifeUploader - 09.08/18
ver 0.0.0.2に更新。アップロードするフォルダが指定出来るようになりました。アップロードツール名(FotolifeUploader)が利用されるようになりました。フォルダ指定は再設定が必要なので、前のバージョンを使っている方はsettings.xmlを削除して、再度設定し直してください。あとは間抜けだったUploadToFotolifeメソッドを手直ししたり。
私自身が、そもそもフォトライフのヘビーユーザーではないので、細かいところに気が利いてないかもですね……。そういうのは、よくない。というわけで、当分はFotolifeをちゃんと利用しようキャンペーンを張ることにします。なので、デジタル一眼を買う。と言いたいのだけど、何か微妙なのよねん。いや、そもそも引き籠って家から出ないので撮影するものがないので。かといって熱帯魚や食虫植物とかフィギュアとか、撮影に適した趣味があるわけでもなく。困った困った。まあ、考えます。食虫植物を育てる方向で(?) 部屋が殺風景なので何かは入れたいのだけど、手間はかけたくない。ううむ、難しい。
LINQ to XML
アップロードにはAtomAPIを利用しているので、XMLです。つまりLINQ to XMLの出番です。出力結果がこんな感じなので、そこから逆に考えると……
<entry xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/atom/n s#"> <title>タイトル</title> <content mode="base64" type="image/jpeg">画像BASE64</content> <generator>FotolifeUploader</generator> <dc:subject>フォルダ名</dc:subject> </entry>
XElementは、Namespaceの利用が少しややこしいんですよね。最初引っかかりました。「XNamespace.Xmlns + “接頭辞”」で登録できます。
XNamespace ns = "http://purl.org/atom/ns#"; XNamespace dc = "http://purl.org/dc/elements/1.1/"; var xml = new XDocument(new XDeclaration("1.0", "UTF-8", null), new XElement(ns + "entry", new XAttribute(XNamespace.Xmlns + "dc", dc), new XElement(ns + "title", "タイトル"), new XElement(ns + "content", new XAttribute("mode", "base64"), new XAttribute("type", "image/jpeg"), "画像BASE64"), new XElement(ns + "generator", "FotolifeUploader"), new XElement(dc + "subject", "フォルダ名") )); Console.WriteLine(xml); // 出力確認、DeclarationはToStringでは出力されない
少し独特ですが、ほとんど1:1で対応させられるので慣れるとサクサク書けます。非常に快適。個人的にはXMLリテラル的なものよりも好き。Linqがあってほんと良かった……。で、Declarationを出力したい場合の話に続く。(hatena (diary ’Nobuhisa))にもあるように、ToStringでは出力されないのでSaveを使う、と……
var sb = new StringBuilder(); var sw = new StringWriter(sb); xml.Save(sw); Console.WriteLine(sb); // UTF-16になる
これでencodingがUTF-16になるのは、Saveメソッド呼ぶとDeclarationは作りなおしているから。.Save(”string fileName”)ではXDeclarationのエンコーディングを見て、それで保存するけれど、それ以外の場合はXDeclaration無視で再構築される。XDocumentというかXmlWriterのほうの話でしょうか。実際にファイル出力してみると分かる。
var fs = new FileStream(@"C:\text.xml", FileMode.Create); var sw = new StreamWriter(fs, Encoding.GetEncoding("x-mac-turkish")); xml.Save(sw);
出力先のエンコードに合わせてくれる、のを便利と見るか、むしろ気が利かない、Writer部分もC#3.0に合わせて作りなおせ、なのかは不明。まあ、嘘エンコード宣言は許しませんよってことですかね。じゃあどうするか、って言ったら
// これで別に何も問題ないと思います、文字列として吐くんだからToStringでいいと思ふ var xmlString = string.Format("{0}{1}{2}", xml.Declaration, Environment.NewLine, xml); Console.WriteLine(xmlString); // ToStringがどうしても嫌ならMemoryStream経由で、とか? string result; var encoding = Encoding.UTF8; using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms, encoding)) { xml.Save(sw); result = encoding.GetString(ms.ToArray()); } Console.WriteLine(result); // 望み通りのUTF-8で出力されてます
結論は、普通にToStringでいいんじゃないかな、と。ToStringメソッドだけではXmlWriterSettingsで言うところのOmitXmlDeclarationを設定出来ないから、デフォルトでは付加しないようにしてる。削除は無理だけど、追加なら簡単だから。XmlDeclarationを付加したい時は別途、自分でくっつければいい。というだけのお話かなー? ToStringで一発で終わらせられないからStringBuilder使って組み立てるってのは、何でそうなるの?と、とても思った。ついでにもう一つ。
// こんなXElementがあるとして var xElement = XElement.Parse("<hoge>3</hoge>"); // intとして値を取り出す時は var num1 = int.Parse(xElement.Value); // これダメ。 var num2 = (int)xElement; // こう書こう。
です。LINQ to XMLは既存のものを上手く使ってシンプルに書けるように作られてる。気がする。このキャストもそうだし、ToStringもそう。Parseは頻繁に行うから汚くなるよね→キャストでよくね? 文字列化はよくやるけどSaveもXmlWriterSettingsも面倒くさいよね→ToStringでよくね? といった感じ。関数型構築もそうだけど、今までのもの(XmlDocument)を踏まえて、よく練り直されているなー、と思います。
