WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

コレクションを表現した JSON のデシリアライズ

by WebSurfer 3. June 2020 15:49

以下のようなコレクション(配列)を表現した JSON 文字列を C# のオブジェクトにデシリアライズするときにハマった話を書きます。

こんなことにハマるのは無知だからだと言われそうですが、また無駄な時間を費やさないように備忘録として書いておくことにしました。

[
  {
    "id":1,
    "text":"project #1",
    "start_date":"2020-05-06",
    "end_date":"2020-05-26",
    "user":3,
    "duration":20,
    "parent":0,
    "progress":0
  },
  {
    "id":2,
    "text":"Task #1",
    "start_date":"2020-05-06",
    "end_date":"2020-05-16",
    "user":0,
    "duration":10,
    "parent":1,
    "progress":0
  },
  {
    "id":3,
    "text":"Task #2",
    "start_date":"2020-05-16",
    "end_date":"2020-05-26",
    "user":0,
    "duration":10,
    "parent":1,
    "progress":0
  }
]

先の記事「JSON 文字列から C# のクラス定義生成」で書きましたように、Visual Studio を使って JSON 文字列から C# のオブジェクトのクラス定義を生成することができます。

上の JSON 文字列から Visual Studio で C# のクラス定義を生成すると以下の通りとなります。

public class Rootobject
{
    public Class1[] Property1 { get; set; }
}

public class Class1
{
    public int id { get; set; }
    public string text { get; set; }
    public string start_date { get; set; }
    public string end_date { get; set; }
    public int user { get; set; }
    public int duration { get; set; }
    public int parent { get; set; }
    public int progress { get; set; }
}

Newtonsoft.Json と JavaScriptSerializer で上に書いた JSON 文字列を Rootobject にデシリアライズしてみます。コード例は以下の通りです。

// Newtonsoft.Json
var result1 = JsonConvert.DeserializeObject<Rootobject>(jsonText);

// JavaScriptSerializer
var serializer = new JavaScriptSerializer();
var result2 = serializer.Deserialize<Rootobject>(jsonText);

そうすると、Newtonsoft.Json では JsonSerializationException 例外が、JavaScriptSerializer では InvalidOperationException 例外がスローされます。Newtonsoft.Json が表示するエラーメッセージは以下の通りでした。

Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ConsoleAppJson.Rootobject' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path '', line 1, position 1.'

これによると Rootobject ではダメで、List<Class1> にデシリアライズしろということのようです。やってみたら確かに List<Class1> にはデシリアライズできました。

なお、上のような配列だけの JSON 文字列ではなくて、例えば { "data": [ { ... },{ ... },{ ... } ] } のような形の JSON 文字列にした場合は、Visual Studio により生成される C# のクラス定義はほぼ同じになりますが、Rootobject にデシリアライズできます。

[ { ... },{ ... },{ ... } ] という形の JSON 文字列だけが要注意のようです。

.NET Framework 版の ASP.NET MVC は JavaScriptSerializer を、ASP.NET Web API は Newtonsoft.Json を使っているので、上のような JSON 文字列を送信してアクションメソッドに渡す場合が自分的に要注意だと思いました。

Tags: , ,

.NET Framework

BOM 無し UTF-8 と StreamReader

by WebSurfer 20. May 2020 14:36

Windows 10 のあるバージョンから、メモ帳でテキストを保存する際の文字コードのデフォルトが ANSI から UTF-8 に変わりました。下の画像を見てください。

メモ帳の文字コード

上の画像で、[ANSI] は「BOM なし Shift_JIS」、[UTF-8] は「BOM なし UTF-8」となります。それ以外はすべて BOM つきとなります。

その変更で気になっていたのは、以前のメモ帳のデフォルト [ANSI] 即ち「BOM なし Shift_JIS」を期待して StreamReader コンストラクタの Encoding を Shift_JIS に指定した以下のようなコードを使っている場合、新しいメモ帳のデフォルト「BOM なし UTF-8」で保存されたファイルを読むとどうなるかということです。

using (StreamReader reader = new StreamReader(
                             dir + "UTF8withoutBOM.txt", 
                             Encoding.GetEncoding("Shift_JIS")))
{
    Console.WriteLine(reader.ReadToEnd());
}

ダメだろうとは思いながらも、念のため試してみましたがやっぱり当たり前にダメでした。下の画像の一行目がそれで、文字化けしています。

結果

上の画像の二行目以降は、順に、メモ帳で保存するとき [UTF-8 (BOM つき)]、[ANSI]、[UTF-16 BE]、[UTF-16 LE] を選んだものです。

[UTF-8] は「BOM なし UTF-8」で、BOM が付いてないので StreamReader コンストラクタの第 2 引数に指定された Shift_JIS としてデコードされ、その結果文字化けしています。

[ANSI] は「BOM なし Shift_JIS」で、これも BOM なしですが、StreamReader コンストラクタの第 2 引数で正しく指定されている通り Shift_JIS としてデコードするので問題なしです。

[UTF-8] と [ANSI] 以外は有効な BOM が付いており、StreamReader は BOM で正しく文字コードが判定できますので、判定した通りデコードされて問題なしという結果になっています。

StreamReader コンストラクターの詳しい説明は左のリンクをクリックして Microfost のドキュメントを見てください。

少し説明しておくと、StreamReader コンストラクタ (String, Encoding) のように Encoding を引数に指定するオーバーロードでは、ファイルの最初の 4 バイトを見てエンコーディングの検出を試み、適切な BOM で始まる場合、それによって UTF-8、リトルエンディアン Unicode、ビッグエンディアン Unicode、リトルエンディアン UTF-32、およびビッグエンディアン UTF-32 を自動的に判別し、それ以外の場合は Encoding に指定されたエンコードが使用されるそうです。

ちなみに BOM は 16 進数表現で以下の通りとなります。

  • UTF-8:    EF BB BF
  • UTF-16BE: FE FF
  • UTF-16LE: FF FE
  • UTF-32BE: 00 00 FE FF
  • UTF-32LE: FF FE 00 00

メモ帳のデフォルトが「BOM なし Shift_JIS」だった時代は、メモ帳で作るテキストファイルを読む場合、StreamReader コンストラクタの Encoding に Shift_JIS 指定を指定しておけば文字化けに悩まずに済みました。デフォルトが「BOM なし UTF-8」に変わった今はそうはいかないようです。

Tags: , ,

.NET Framework

応答ヘッダが 64KB を超えるとエラー

by WebSurfer 2. September 2019 12:00

HttpClient を使った HPPT 通信で、応答ヘッダが 64KB を超えると WebException 例外がスローされるという話を備忘録として書いておきます。

(元の話は Teratail のスレッド「GetAsync処理時のメッセージの長さが制限の解消方法について」のもので、実際に自分が経験した訳ではなく聞いた話です)

同様な問題は HttpWebResponse / HttpWebRequest を使った時から起こっていた問題だそうで、応答ヘッダが 64KB を超えると WebException がスローされ、以下のエラーメッセージが出るそうです。

"接続が切断されました: メッセージの長さが制限を超えています。"

英文では、

"The underlying connection was closed: The message length limit was exceeded."

応答ヘッダが 64KB を超えるというのはレアなのか、日本語のエラーメッセージでググっても参考になる記事はヒットしませんでした。

でも、英語圏まで検索範囲を広げる(英文でググる)と HttpWebResponse / HttpWebRequest でこの問題に遭遇した人はいるようで、WebException: "The message length limit was exceeded" 他の記事がヒットします。

その記事に書いてある解決策は、HttpWebRequest の MaximumResponseHeadersLength プロパティを -1 (無制限) に設定することだそうです。(未検証・未確認です)

HttpClient を使う場合は、.NET Framework 4.7.1 以降ですが、HttpClientHandler クラスMaxResponseHeadersLength プロパティを使って応答ヘッダのサイズの許容最大値を設定できるそうです。

// Create an HttpClientHandler object
HttpClientHandler handler = new HttpClientHandler();
handler.MaxResponseHeadersLength = 128;  // 128KB

// Create an HttpClient object
HttpClient client = new HttpClient(handler);

.NET 4.5 では MaxResponseHeadersLength プロパティは使えず .NET 4.7.1 で使えるようになったということは、HttpWebRequest / HttpWebResponse で起こっていた問題に対応できないことを指摘されて追加したのかもしれませんね。(想像です)

Tags: , , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  June 2020  >>
MoTuWeThFrSaSu
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

View posts in large calendar