JSONの保存形式について

タグの編集
投稿者 まほ  (学生) 投稿日時 2021/6/28 21:57:43
お世話になっています。
以前ご質問させてもらった内容「http://rucio.cloudapp.net/ThreadDetail.aspx?ThreadId=30599」の続きです。

るきおさんの記事「https://www.umayadia.com/Note/Note015JSONSerialize.htm」を参考に編集したDataTableからJSONの生成を試みているのですが、下記ように別々のキーに分けて二階層で保存するにはどのように記述すればよいのでしょうか?
(サンプルではImageキーにネストしていく方法しか分かりませんでした)

-------------------------------------------------------------------------
例:
{
    "list": {
        "grouplist": [
            "fruit",
            "animal",
            "flower"
        ],
        "fruit": [
            "リンゴ",
            "バナナ",
            "イチゴ"
        ],
        "animal": [
            "イヌ",
            "ネコ",
            "ウサギ"
        ],
        "flower": [
            "バラ",
            "ユリ",
            "キク"
        ]
    },
    "list2": {
        "grouplist": [
            "fruit",
            "animal",
            "flower"
        ],
        "fruit": [
            "リンゴ",
            "バナナ",
            "イチゴ"
        ],
        "animal": [
            "イヌ",
            "ネコ",
            "ウサギ"
        ],
        "flower": [
            "バラ",
            "ユリ",
            "キク"
        ]
    },
    "list3": {
        "grouplist": [
            "fruit",
            "animal",
            "flower"
        ],
        "fruit": [
            "リンゴ",
            "バナナ",
            "イチゴ"
        ],
        "animal": [
            "イヌ",
            "ネコ",
            "ウサギ"
        ],
        "flower": [
            "バラ",
            "ユリ",
            "キク"
        ]
    }
}
-------------------------------------------------------------------------

実際の各キー名は固定ではないため「DictionaryからJSONを生成する」の方法で試みています。
JSONの生成は改行とインデントありでお願いします。
 
投稿者 るきお  (社会人) 投稿日時 2021/6/29 08:20:15
こんにちは。

この記事の「https://www.umayadia.com/Note/Note015JSONSerialize.htm」の「DictionaryからJSONを生成する」になぞらえて書くと次のようにできます。

Dim content As New Dictionary(Of StringObject)

content.Add("list"New Dictionary(Of StringObjectFrom {
            {"grouplist", {"fruit""animal""flower"}},
            {"fruit", {"リンゴ""バナナ""イチゴ"}},
            {"animal", {"イヌ""ネコ""ウサギ"}},
            {"flower", {"バラ""ユリ""キク"}}
         })

content.Add("list2"New Dictionary(Of StringObjectFrom {
            {"grouplist", {"fruit""animal""flower"}},
            {"fruit", {"リンゴ""バナナ""イチゴ"}},
            {"animal", {"イヌ""ネコ""ウサギ"}},
            {"flower", {"バラ""ユリ""キク"}}
         })

Dim jsonText As String = JsonConvert.SerializeObject(content, Formatting.Indented)

Debug.WriteLine(jsonText)


list3 は省略しました。
投稿者 るきお  (社会人) 投稿日時 2021/6/29 08:24:38
サンプルを書いているうちに「方法4:コレクション初期化子を使ってJSONを生成する」の方に寄せてしまいました。
まほさんのJSONだと、方法1か方法4が扱いやすそうですね。
投稿者 まほ  (学生) 投稿日時 2021/6/29 12:32:12
るきおさん有難うございます。
方法4も書き方がシンプルなのですごく分かり易かったです。

続けてご質問ですが、fruit,animal,flowerのグループは実際には3つではなく、データの追加削除によってグループ数が変動するので動的にキーと値を追加したいのですが、その場合どのように記述すればよいのでしょうか?
イメージ的にはこのような感じです。

    Private JsonDataTable As New DataTable

    Private Sub SaveJson(ByVal FilePath As String)

        Dim Grouplist As New List(Of String)

        'DataTableから列(グループ名)を取得 
        For Each Column As DataColumn In JsonDataTable.Columns
            Grouplist.Add(Column.ColumnName)
        Next

        Dim content As New Dictionary(Of StringObject)
        content.Add("list"New Dictionary(Of StringObjectFrom {
                 {"grouplist", {Grouplist}}
                 })

        'ここから動的にキーを追加したい 
        For i As Integer = 0 To JsonDataTable.Columns.Count - 1
                {Grouplist(i), {JsonDataTable.AsEnumerable().Select(Function(row) row(Grouplist(i))).ToArray()}}
                })
        Next

        Dim jsonText As String = JsonConvert.SerializeObject(content, Formatting.Indented)

        Debug.WriteLine(jsonText)

        'JSONをファイルに保存 
        Dim enc As Encoding = Encoding.UTF8
        My.Computer.FileSystem.WriteAllText(FilePath, jsonText, False, enc)

    End Sub
投稿者 るきお  (社会人) 投稿日時 2021/6/29 20:17:49
list にだけ項目を追加したいのならこのような感じです。
最初の例ではObjectとしていた型を操作しやすいようにIEnumerable(Of String)等に変更しています。

Dim content As New Dictionary(Of String, Dictionary(Of String, IEnumerable(Of String)))

content.Add("list"New Dictionary(Of String, IEnumerable(Of String)) From {
            {"grouplist", {"fruit""animal""flower"}},
            {"fruit", {"リンゴ""バナナ""イチゴ"}},
            {"animal", {"イヌ""ネコ""ウサギ"}},
            {"flower", {"バラ""ユリ""キク"}}
         })

content.Add("list2"New Dictionary(Of String, IEnumerable(Of String)) From {
            {"grouplist", {"fruit""animal""flower"}},
            {"fruit", {"リンゴ""バナナ""イチゴ"}},
            {"animal", {"イヌ""ネコ""ウサギ"}},
            {"flower", {"バラ""ユリ""キク"}}
         })

'list に要素を追加 
content("list")("grouplist") = content("list")("grouplist").Append("planet")
content("list").Add("planet", {"Earth""Mars""Jupiter"})


Dim jsonText As String = JsonConvert.SerializeObject(content, Formatting.Indented)

Debug.WriteLine(jsonText)



list と list2 の値を連動させたい(=同じ1つの実体を指すようにしたい)のであれば次のような感じです。

Dim content As New Dictionary(Of String, Dictionary(Of String, IEnumerable(Of String)))

Dim manifest = New Dictionary(Of String, IEnumerable(Of String)) From {
            {"grouplist", {"fruit""animal""flower"}},
            {"fruit", {"リンゴ""バナナ""イチゴ"}},
            {"animal", {"イヌ""ネコ""ウサギ"}},
            {"flower", {"バラ""ユリ""キク"}}}

content.Add("list", manifest)
content.Add("list2", manifest)

manifest("grouplist") = manifest("grouplist").Append("planet")
manifest.Add("planet", {"Earth""Mars""Jupiter"})


Dim jsonText As String = JsonConvert.SerializeObject(content, Formatting.Indented)

Debug.WriteLine(jsonText)
投稿者 まほ  (学生) 投稿日時 2021/6/29 22:40:02
ありがとうございます。
ご提示されたコードを試してようとしたところ、「'Append' は 'IEnumerable(Of String)' のメンバーではありません。」とエラーが出てお試しできなかったです。
Option Explicitをオフにしても同様のエラーが出るためご確認をお願いします。
投稿者 るきお  (社会人) 投稿日時 2021/6/30 08:21:48
使っているフレームワークが古いのかもしれませんね。
私は.NET 5 のコンソールアプリケーションで試していますが、特に何もしなくても使えています。

フレームワークのバージョンを次のように変更できますか?
.NET Framework であれば 4.7.1 以上
.NET/.NET Core であれば .NET 5以上

それでもダメでしたら、念のため冒頭に Imports System.Linq を付けてみてください。

どうしても動かないようなら Append を使っている行はコメントにしてください。
この行は今回ご質問の件とは本質的な関係はないです。
投稿者 まほ  (学生) 投稿日時 2021/6/30 18:31:18
るきお様
フレームワークのバージョンを確認してみたところ4.5と古かったため、ご指摘通りVB2019で一番バージョンの高い4.7.2へ変更したところエラーがなくなりました。

本題ですが、一つ目のサンプル下記二行の「list」と「grouplist」の固定キー以外を変数に変えて、ループで必要な分だけ動的に定義することで生成したJSONが思い通りの結果になりました。

content("list")("grouplist") = content("list")("grouplist").Append("planet")
content("list").Add("planet", {"Earth", "Mars", "Jupiter"})

ここまでご丁寧に教えていただいてありがとうございました。
また躓いた際にはよろしくお願いします。