【入門編】VB.NETにおけるXML/JSONシリアライゼーションの落とし穴:Newtonsoft.JsonやSystem.Text.Jsonを用いた、Windows Formsアプリの設定ファイル・通信データ永続化 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク
スポンサーリンク

【極限の知見】VB.NETでデータを自在に操る:JSONシリアライゼーションの罠と回避術

「Excelのマクロなら、セルに値を書き込むだけでデータの保存はできた。でも、Windows Forms(WinForms)で本格的なアプリを作ろうとすると、どうやって複雑な設定や大量のデータを保存すればいいのかわからない……」

もしあなたが今、そんな壁にぶつかっているなら、おめでとうございます。あなたは今、「単なる操作の自動化」から「ソフトウェア設計」へと進化する境界線に立っています。

こんにちは。今日は、VB.NETで業務アプリケーションを開発する上で避けては通れない、しかし多くの初学者が泥沼にハマる「シリアライゼーション(直列化)」という魔法についてお話ししましょう。

この技術をマスターすれば、複雑に絡み合った業務データも、まるで魔法のように一瞬でファイルへ保存し、再び元の姿で呼び出すことができるようになります。

1. シリアライゼーションとは「分解と組み立て」である

まず、言葉の定義を整理しましょう。難しく考える必要はありません。

  • シリアライゼーション(直列化): メモリ上にある「オブジェクト(複雑なデータの塊)」を、ファイルに書き出せる「テキスト(JSONやXML)」に変換すること。
  • デシリアライゼーション(逆直列化): テキストファイルを読み込み、再びメモリ上の「オブジェクト」として復元すること。

イメージとしては、「完成したレゴの模型(オブジェクト)」を、一度「説明書とバラバラのパーツ(JSONテキスト)」にして箱に詰め(シリアライズ)、後で「説明書を見て元の模型に戻す(デシリアライズ)」ようなものです。

WinFormsアプリでは、ユーザーが設定した項目、通信で受け取った複雑なデータ、履歴情報などをこの方法で保存します。

2. 実践:ネスト構造を持つ設定ファイルの設計

単なる「名前」や「数値」だけでなく、「設定の中に、さらに詳細な設定が含まれている(ネスト構造)」という、実務でよくあるケースを想定してコードを書いてみましょう。

ここでは、現代のデファクトスタンダードである `Newtonsoft.Json` (Json.NET) を使用します。

設計するデータ構造

  • `AppConfig` (全体の設定)
  • `UserName` (ユーザー名)
  • `LastLogin` (最終ログイン日時)
  • `ConnectionSettings` (接続設定:さらに子要素を持つ)
  • `ServerIp` (IPアドレス)
  • `Port` (ポート番号)

実装コード

Imports Newtonsoft.Json
Imports System.IO
Imports System.Text

''' <summary>
''' アプリケーションの接続設定を保持するクラス
''' </summary>
Public Class ConnectionSettings
    ' プロパティは必ず Public である必要があります(重要!)
    Public Property ServerIp As String = "127.0.0.1"
    Public Property Port As Integer = 8080
End Class

''' <summary>
''' アプリケーション全体の設定を保持するメインクラス
''' </summary>
Public Class AppConfig
    Public Property UserName As String = "Guest"
    Public Property LastLogin As DateTime = DateTime.Now
    
    ' ここが「ネスト構造」です。クラスの中に別のクラスを持たせています。
    Public Property Connection As ConnectionSettings = New ConnectionSettings()
End Class

''' <summary>
''' 設定の保存・読み込みを管理するマネージャー
''' </summary>
Public Class ConfigManager
    Private ReadOnly _filePath As String = "config.json"

    ''' <summary>
    ''' 設定をJSONファイルとして保存する
    ''' </summary>
    Public Sub SaveConfig(config As AppConfig)
        Try
            ' Formatting.Indented を指定することで、人間が見やすい(改行された)JSONになります
            Dim json As String = JsonConvert.SerializeObject(config, Formatting.Indented)
            File.WriteAllText(_filePath, json)
            Console.WriteLine("設定を保存しました。")
        Catch ex As Exception
            ' 実務ではログ出力などを行い、ユーザーにエラーを知らせます
            Throw New Exception($"保存中にエラーが発生しました: {ex.Message}")
        End Try
    End Sub

    ''' <summary>
    ''' JSONファイルから設定を読み込む
    ''' </summary>
    Public Function LoadConfig() As AppConfig
        Try
            If Not File.Exists(_filePath) Then
                ' ファイルがない場合はデフォルトの設定を返す
                Return New AppConfig()
            End If

            Dim json As String = File.ReadAllText(_filePath)
            ' テキストをオブジェクトに変換(デシリアライズ)
            Dim config As AppConfig = JsonConvert.DeserializeObject(Of AppConfig)(json)
            
            ' 万が一、ファイルが空だった場合の対策
            Return If(config, New AppConfig())
        Catch ex As Exception
            Throw New Exception($"読み込み中にエラーが発生しました: {ex.Message}")
        End Try
    End Function
End Class

3. 【極限の知見】プロが教える「3つの落とし穴」

ここからが本番です。コードが動いたからといって安心しないでください。シリアライゼーションには、ベテランでも時折足元をすくわれる「罠」が潜んでいます。

罠①:プロパティの「公開範囲(Visibility)」問題

初心者が最も陥りやすい罠です。
シリアライザー(Newtonsoft.Jsonなど)は、`Public` なプロパティしか見ることができません。

  • NG: `Private Property UserName As String`
  • OK: `Public Property UserName As String`

もし `Private` や `Friend` で定義してしまうと、シリアライズした時にその項目が無視され、ファイルには書き出されません。デシリアライズした時に「あれ?データが消えている!」となる原因の9割はこれです。

罠②:循環参照(Infinite Loop)の恐怖

オブジェクト同士が互いに参照し合っている場合に発生します。

  • `User` クラスが `Group` クラスを持っている。
  • `Group` クラスが、リストの中に `User` クラスを持っている。

これをそのままシリアライズしようとすると、「User → Group → User → Group…」と無限ループに陥り、スタックオーバーフロー(アプリの強制終了)を引き起こします。

【回避策】
`[JsonIgnore]` 属性を使い、一方の参照をシリアライズ対象から外すように指示してください。

Public Class Group
    Public Property GroupName As String
    
    <Newtonsoft.Json.JsonIgnore> ' これで無限ループを防ぐ!
    Public Property Members As List(Of User)
End Class

罠③:型(Type)の不一致と「Null」の扱い

JSONはテキストなので、型が緩やかです。
例えば、プログラムでは `Integer`(数値)として定義している場所に、JSONファイルに `”ABC”`(文字列)と書かれていた場合、デシリアライズは容赦なくエラーを吐いて止まります。

また、ネストしたオブジェクトが `Nothing` (Null) の状態で保存・読み込みを行うと、`NullReferenceException` を誘発します。

【回避策】

  • `LoadConfig` メソッドのように、読み込み後に必ず `If(config, New AppConfig())` といった形で、「もし空(Nothing)だったら初期値を生成する」というガードコードを徹底してください。

4. まとめ:ここをクリアすれば、基本はバッチリです

シリアライゼーションをマスターすることは、データの「永続性(アプリを閉じてもデータが消えないこと)」を手にすることです。

1. 構造を整理する: クラスを適切に分け、ネスト構造を作る。
2. Publicにする: シリアライザーに見えるようにする。
3. 例外処理を忘れない: ファイル操作は常にエラーのリスクがある。
4. 循環参照とNullに備える: プロの仕事は「動かない時」の対策にあります。

最初は難しく感じるかもしれません。でも、一度この「仕組み」を理解してしまえば、Excelマクロの世界では決して到達できなかった、堅牢で高度なアプリケーション開発の扉が開かれます。

一歩ずつ、着実に進んでいきましょう。応援していますよ!

タイトルとURLをコピーしました