トップ «前の日記(2011-12-16) 最新 次の日記(2011-12-23)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2011-12-17

_ 安全なDataContractJsonSerializerの使い方または自分を疑う前にマイクロソフトを疑え

ちょっとしたパラメータをJsonにしようとして、ExcelからVBAでADODB.Streamを使ってファイルに書き出した。

それをC#(.NET Framework 4)でデシリアライズしようとするとエラーとなる。

ハンドルされていない例外: System.Runtime.Serialization.SerializationException: オブジェクト 型 j2o.JsonTest の のシリアル化を解除しているときにエラーが発生しました。予期しない文字 'i' が見つかりました。 
---> System.Xml.XmlException: 予期しない文字 'i' が見つかりました。

再現コードは面倒なのでVBSでリテラル吐き出しとして以下に示す。

set ads = CreateObject("ADODB.Stream")
ads.Charset = "UTF-8"
ads.Open
ads.WriteText "{""members"": [{""name"":""dede"", ""age"":1},{""name"":""bebe"", ""age"":2},{""name"":""cece"",""age"":3}]}"
ads.SaveToFile "test.json"
ads.Close

iとな? が、iなんてないはずだが。

utf-8だからcmdコンソールでtypeしても正しく表示されるとは考えられないので(本物は日本語も使っている)メモ帳で見てみる。

{"members": [{"name":"dede", "age":1},{"name":"bebe", "age":2},{"name":"cece","age":3}]}

予想通りだし、iなんて使ってない。文字コードもUTF-8なのは、名前をつけて保存のエンコーディングがutf-8になっていることで確認した。

というわけで、不思議に思いながらもVBAやExcelシートを調べるのであった。

だが、いくら調べてもわからん。しょうがないのでC#側も見てみる。

これも同じく簡略化してコンソールベースとしたものを示す。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
namespace j2o
{
    class Program
    {
        static void Main(string[] args)
        {
            FileInfo f;
            if (args.Length == 0
                || !(f = new FileInfo(args[0])).Exists)
            {
                System.Console.WriteLine("usage: j2o jsonfile");
                return;
            }
            using (var fin = f.OpenRead())
            {
                var des = new DataContractJsonSerializer(typeof(JsonTest),
                    new Type[] { typeof(JsonTestItem) });
                foreach (var i in (des.ReadObject(fin) as JsonTest).members)
                {
                    System.Console.WriteLine(i.name);
                }
            }
        }
    }
    public class JsonTestItem
    {
        public string name { get; set; }
        public int age { get; set; }
    }
    public class JsonTest
    {
        public JsonTestItem[] members { get; set; }
    }
}

[DataContract]属性を付けたり外したり、いろいろ試す。が、どう考えても正しいコードだ。バージョンの不整合とかかなぁとか不思議に思いながらADOのバージョンまでチェックしたりする。

が、そもそもiがおかしいといわれているのに、iなど無いのだ。

そのうちに、コンソールによってはiではなく、iの上がトレマだということに気付いた。コマンドラインではなく、デバッガ出力だとトレマになる。

ということは存在していない(あるいは目に見えない)バイナリ文字を見つけて文句を垂れているってことだな、と気付いた。

とすれば、答は明確だ。

UTF-16用のBOMを後先考えずにUTF-8への変換式に従ってこしらえたMS特有のBOMだ。

もちろん、そんな変なものはMS固有なので、基本はインターネットの向こうの世界を相手にするDataContractJsonSerializerがそんなものを頭から考えていなくても責めることはできないかも知れない。

でも、自社の代表的なプロダクトであるメモ帳で作ったテストデータくらいは食わせてみろよ、と思うけどな。

                fin.Position = (fin.ReadByte() == 0xef) ? 3 : 0;

をusingブロックの最初の行に追加して終結。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|

ジェズイットを見習え