2021年6月19日土曜日

FAC製作記 その3 JsonSerializer.SerializeAsync()で何も出力されない

本題の前に少しだけ、これまでの反省といいますか、ちょっとした注意書きを。
前回が前提となる事柄に分量や気力などを吸い取られまくったので、これからはそういう事柄は適当にカットします。

ここから本題。
これまでの2回はダイアログでしたが、今回はJSONの処理についてです。
FACでは設定した内容をsettings.jsonというファイルにJSON形式で保存しています。
起動したときにファイルから設定を読み込んで、画面の「設定を保存」ボタンをクリックするとファイルに設定を書き込むようになっています。 (画面についてはFACについての最初の投稿にスクショを貼っています)

【FACでのJSONの扱い方】
.NET 5対応の前後でJSONのシリアライズ/デシリアライズに使っているものが異なっています。
.NET 5対応前:Jil
.NET 5対応後:JsonSerializer
Jilは個人が作っているライブラリ、JsonSerializerは.NET Core 3.0/C# 8.0から追加された標準機能です。

今回の課題と解決策は以下の通りです。

【課題】
JsonSerializerの非同期シリアライズ&書き込みを使うと何も出力されない。

【解決策】
JsonSerializerはJSONのシリアライズ/デシリアライズにだけ使う。
ファイルの読み書きはStreamReader/StreamWriterに任せる。

今回は.NET 5対応のときに作ったテスト用アプリのソースコードを見ていただいたほうが早いと思います。

まずは読み込みから。


読み込みのメソッド全部でこれ。
コメントアウトしているDeserializeAsync()がストリームを使った非同期デシリアライズ&読み込みですが、こちらはうまくいきました。

一方の書き込みが以下になります。


try-catchのtry部分しか収まりませんでした。
コメントアウトしたコードにあるSerializeAsync()が非同期のシリアライズ&書き込みメソッドです。
例外は発生しなかったのですが、「課題」に書いたとおり、書き込み完了後にファイルを開いても何も出力されていない、という現象が発生しました。

【試行錯誤】
まずはFileStreamを作るときに使ったメソッドが良くないのではないか?と疑いました。
スクショの77~79行目で、Open()を使ったり、オプション指定するコンストラクターを使ったりと試しています。
これらはいずれも効果なく、ファイルには何も出力されない状態が続きました。

次にそもそもシリアライズは成功しているのか?ということで、Serialize()でシリアライズした結果をデバッグ出力させてみることにしました。
スクショの82と83行目です。
こちらは成功して、JSON文字列が出力されました。

以上の2つから、JsonSerializerはシリアライズだけ担当してもらい、ファイルへの書き込みは別にすればうまくいくのでは?という発想が出てきます。
それを試したのが90~92行目です。
コメントにある通りで、ファイルにはJSON文字列が出力されていました。成功です。

さて、これで読み込みはすべてJsonSerializerで、書き込みはJsonSerializer+StreamWriterで処理している状態になりました。
これでもちろん動くのですが、書き込みがJsonSerializer+StreamWriterなら、読み込みはJsonSerializer+StreamReaderにしたほうが、対照というか見栄えというか、きれいに見えませんか?

ということで今度は読み込み側に手を加えてみます。
ファイルからの読み込みをStreamReaderに受け持ってもらい、JsonSerializerはデシリアライズだけ担当してもらいます。
読み込みの52と53行目ですね。
実行した結果は変更前と変わらず、こちらは特に引っかかりなく書き換えに成功しました。

なので、今回はJsonSerializerを使う範囲をJSONとオブジェクトの変換に限定し、ファイルの読み書きはStreamReader/StreamWriterを使うことで解決としました。

【考察】
最初はファイルへの書き込みが終わる前にファイルを開いてしまったのが原因かもしれないと思ったのですが、それなら書き込めずに例外が発生するはずですので、この可能性はないでしょう。
次にGitHubでJsonSerializerのソースコードを見てみたのですが、わかりませんでした。
処理をたどっていくと、どうやらストリームに直接書き込まずにバッファリングしているようですが、バッファリングしようがしまいが最終的に出力先に書き込むのは変わらないはずなので、これのせいだとも思えません。
なんだったんでしょうね・・・?

【参考資料】

今日の1曲:


0 件のコメント:

コメントを投稿