C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: json形式のデータの取得ができない場合がある


(過去ログ 172 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■99176 / inTopicNo.1)  json形式のデータの取得ができない場合がある
  
□投稿者/ 紫モップ (1回)-(2022/02/17(Thu) 04:00:45)

分類:[VB.NET/VB2005 以降] 

紫モップと申します。
Visual Basic2022から始めた初心者です。ここで質問をさせていただくのも初めて故至らない点は多くあると思いますが,どうぞよろしくお願いいたします。

開発環境
Visual Studio 2022
Windowsフォームアプリ
NuGetよりNewtonsoft.Jsonをインストール済み

デザイン:Form1, Button1 のみ

問題点:json形式のデータが取得できない場合がある

状況:
現在,気象庁のサイト上にある気象データ(json形式)を呼び出して内部の各種値を取り出そうと試みております。
不明な点を調べながらコードを書いていった結果、以下のコードを用いることでサイトから一週間分の気象予測データ(および各値)を取得することができました。

Public Class Form1

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Dim url As String = "https://www.jma.go.jp/bosai/forecast/data/overview_week/130000.json"      '1週間分のデータが載っているページ

        Using client As New System.Net.Http.HttpClient()

            Using response As System.Net.Http.HttpResponseMessage = client.GetAsync(url).Result

                Dim responseBody As String = response.Content.ReadAsStringAsync().Result        'レスポンス全体を文字列で取得

                '文字列をJObjectに変換。後述のエラーが発生する場所
                Dim oResponse As Newtonsoft.Json.Linq.JObject = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(responseBody), Newtonsoft.Json.Linq.JObject)

                Dim publishingOffice As String = oResponse("publishingOffice").ToString     '各値を取得

                MessageBox.Show($"発行 = {publishingOffice}")         '値を出力

            End Using

        End Using

    End Sub

End Class

(参考にさせていただいたサイト:https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample280InvokeRestWebAPI.htm#A2 GET のWebAPIを呼び出してJSONの戻り値を解析する より)


しかし,上記コードのページ名をhttps://www.jma.go.jp/bosai/forecast/data/forecast/130000.json(より詳細な気象データが載っているページ。同じくjson形式)に変更して
実行すると以下のようなエラーが発生し,データを取得することができませんでした。


ユーザーが処理していない例外
System.InvalidCastException: 'Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'Newtonsoft.Json.Linq.JObject'.'


恥ずかしながらエラーについて調べても理解が及ばず,自分の力では解決することが難しそうであったため,以下の点を教えていただきたく参上した次第です。

・ページは違えど同じjson形式であるはずなのに,なぜ異なる結果となるのでしょうか?
・上記コードで修正すべき点があるならば,それはどこなのでしょうか?

まだまだ不勉強であり至らない点も多い若輩者ではありますが,ぜひとも皆様のお知恵を貸してはいただけないでしょうか。




引用返信 編集キー/
■99177 / inTopicNo.2)  Re[1]: json形式のデータの取得ができない場合がある
□投稿者/ くま (163回)-(2022/02/17(Thu) 04:47:35)
紫モップ さん 配列は分かりますか?
以下のJSON形式は単体のデータで配列ではありません。
https://www.jma.go.jp/bosai/forecast/data/overview_week/130000.json

以下のJSON形式は配列のデータで単体ではありません。
https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json

System.InvalidCastException: 'Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'Newtonsoft.Json.Linq.JObject'.'
訳すと
'Newtonsoft.Json.Linq.JObject'という単体形式のJSONObject(単体形式)をJSONArray(配列形式)に変換できないです
というエラーです。

ちょっとよいサンプルが見つからなかったのですが
https://became-free.com/vbdotnet-joson-array-to-class/
https://www.umayadia.com/Note/Note009VBJSON.htm
辺りが参考になるかと思いますが、配列の「List(Of ???)」が分からないと説明できないですね...。
(あとJSON配列についても)
引用返信 編集キー/
■99179 / inTopicNo.3)  Re[1]: json形式のデータの取得ができない場合がある
□投稿者/ WebSurfer (2445回)-(2022/02/17(Thu) 06:45:34)
No99176 (紫モップ さん) に返信

> '文字列をJObjectに変換。後述のエラーが発生する場所

何故 JObject なのですか? 普通は JSON 文字列からクラス定義を作ってそれにデシリアライズし、
そのデシリアライズしたオブジェクトから必要な情報を取得するのが普通です。
引用返信 編集キー/
■99180 / inTopicNo.4)  Re[1]: json形式のデータの取得ができない場合がある
□投稿者/ shu (1274回)-(2022/02/17(Thu) 08:01:04)
2022/02/17(Thu) 08:14:05 編集(投稿者)

No99176 (紫モップ さん) に返信

https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JContainer.htm
ここを見るとクラス階層が分かりますが
JArrayとJObjectはJContainerからの派生となっておりますので相互にキャストすることが出来ません。
DeserializeObjectするときに
JContainerまたはJToken (※JTokenで取得すればJValueもカバーできるのでJTokenの方がよいと思います)
で変換を行いTypeプロパティで判断し
各値を取得するとよいかと思います。




引用返信 編集キー/
■99181 / inTopicNo.5)  Re[1]: json形式のデータの取得ができない場合がある
□投稿者/ WebSurfer (2446回)-(2022/02/17(Thu) 10:43:47)
No99176 (紫モップ さん) に返信

No99179 で「何故 JObject なのですか?」と聞いたのですが参考にされているサイトの記事
がそのようにしていて、それをそのまま真似たのですね。

参考サイトは「REST WebAPIを呼び出す」ことに重点が置かれているのか、取得した JSON
文字列を VB.NET のオブジェクトにデシリアライズして使うということについては不十分だ
と思います。

上にレスのも書きましたが、JSON 文字列からクラス定義を作ってそれにデシリアライズし、
そのデシリアライズしたオブジェクトから必要な情報を取得ことをお勧めします。

クラス定義は以下の記事のように Visual Studio のツールで簡単に可能です。下の記事は
C# ですが VB.NET でも同様にして、VB.NET にクラス定義が得られます。

JSON 文字列から C# のクラス定義生成
http://surferonwww.info/BlogEngine/post/2020/05/10/generate-class-definition-from-json-string.aspx

例えば、https://www.jma.go.jp/bosai/forecast/data/overview_week/130000.json から得
られる JSON 文字列からは以下のクラス定義が得られます。もう一方も定義は少し複雑にな
りますが同様です。

Public Class Rootobject
Public Property publishingOffice As String
Public Property reportDatetime As Date
Public Property headTitle As String
Public Property text As String
End Class

質問者さんのコードの responseBody に API から取得した JSON 文字列が代入されるとして
以下のようにデシリアライズすれば、上に定義された Rootobject クラスのオブジェクトが
戻り値として返されます。

JsonConvert.DeserializeObject(Of Rootobject)(responseBody)

なお、もう一方の https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json
ら取得される JSON 文字列は配列なので、以下の記事のようにしてください。

コレクションを表現した JSON のデシリアライズ
http://surferonwww.info/BlogEngine/post/2020/06/03/deserialize-json-string-which-denote-collection.aspx

引用返信 編集キー/
■99182 / inTopicNo.6)  Re[1]: json形式のデータの取得ができない場合がある
□投稿者/ WebSurfer (2447回)-(2022/02/17(Thu) 10:50:31)
No99176 (紫モップ さん) に返信

本題とは関係ない話ですが、using 句を使って HttpClient の生成 / Dispose を短期間で
何度も繰り返すとソケットの枯渇につながるという問題がありますので注意してください。
(質問者さんのコードではボタンクリックのたびにそれが繰り返されます)

もう一つ、JSON 文字列構造が不定なので特定のクラスにデシリアライズできないが、必要
な情報のある {"name":"value"} の name は事前に分かっているので、それに該当する
value を取得できれば目的は果たせるというようなケースは、以下のようにできます。

JSON 文字列から指定した name の value を取得
http://surferonwww.info/BlogEngine/post/2021/02/11/find-value-by-name-in-json-string.aspx
引用返信 編集キー/
■99186 / inTopicNo.7)  Re[2]: json形式のデータの取得ができない場合がある
□投稿者/ 紫モップ (2回)-(2022/02/17(Thu) 12:13:21)
紫モップです。
くま 様, WebSurfer 様,shu 様, 私のためにお時間を割いていただいて回答をしてくださったこと,心から感謝申し上げます。
まさか短時間の間にこれだけのお返事を頂けるとは思っておらず,確認するのが遅れてしまいました。

本来ならすぐにでも試して,その結果などをお伝えしたいところではありますが,若輩者の私ではお三方の回答をすべて理解する
のには幾分時間がかかりそうな上,諸事情によりしばらく十分な時間が取れそうにないため,お返事が遅れてしまうこととなりそうです。

また,本来ならばお一人ずつお礼を言うべきところなのかもしれませんが,現在進行形で立て込んでいるため,こちらについても改めて
後日させていただければと存じます。

次にお目見えするときのために,今より成長している姿でお会いできるよう精進を重ねたいと思います。
改めまして皆様,ご協力いただき誠にありがとうございました。
引用返信 編集キー/
■99187 / inTopicNo.8)  Re[3]: json形式のデータの取得ができない場合がある
□投稿者/ WebSurfer (2448回)-(2022/02/17(Thu) 16:09:05)
No99186 (紫モップ さん) に返信

> 現在進行形で立て込んでいるため,こちらについても改めて
> 後日させていただければと存じます。

それなら「現在進行形で立て込んで」いない時に質問すべきではないのですか? 突然予定してない
急用ができたという話ならやむを得ないでしょうけど、そういう話ではなさそうですし。


> 次にお目見えするときのために,今より成長している姿でお会いできるよう精進を重ねたいと思います。

それはいつになるのでしょう? なんとなく二度とお会いできない気がしてますが、気のせいですか?
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -