[筆記][OpenData][JSON][Object]取得公開資料平台天氣資料轉物件集合,下拉顯示最新資料

把政府公開資料JSON格式資料取得,轉成物件集合,並透過LINQ對物件集合進行讀取,這樣的範例應該還蠻多人有這樣的需求,小喵就抽個空,寫一下這個範例,提供網友與未來的自己參考~

緣起

這個題目應該是很多人會遇到的,剛好一個空檔,寫個範例,提供網友們與未來的自己參考

目標

  1. 從政府的公開資料平台,取得天氣的資料
  2. 可下拉選擇地區(資料來自取得資料Distinct)
  3. GridView顯示2.選擇地區的相關氣象資料
  4. 取得2.地區的最新一筆資料,放在最新資料區

資料來源

資料由政府的公開資料平台提供,選擇資料的格式是JSON,他的網址如下:

https://opendata.epa.gov.tw/ws/Data/ATM00698/?$format=json

準備工作

Newtonsoft.JSON

由於資料的格式是JSON,因此透過Nuget,預先取得『Newtonsoft.JSON』並安裝好

分析JSON內容(JSON Parser)

直接透過瀏覽器,先呼叫一次該網址(),取得JSON內容,看齊來亂亂的不容易理解

可以透過一個線上的服務,可以搜尋關鍵字『JSON Parser』,或者直接點選以下連結進入

http://json.parser.online.fr/

將瀏覽器中亂亂的JSON全選複製,然後貼到該網頁中左邊處,稍微等一下讓他整理,就可以得到整理好的JSON內容,方便觀察。另外,也順便查看這JSON內容是否有格式上的問題。

小提醒:如果格式有問題,他會有提示。務必確認他的格式是正確的,這樣下面的步驟才不會有問題做白工。

產生類別對應JSON

我們希望可以把JSON轉換成『物件的集合』,轉成『物件集合』後,後續處理就會方便許多,而第一步驟,就是依據JSON的內容,去撰寫對應的類別(Class)

我們新增一個Class,如果您開的是『網站』專案,就在『App_Code』裡面,如果是Web專案,則是在『Models』的資料夾中

選擇性貼上→貼上JSON作為類別

此步驟是這一篇的重點之一,我們新增一個類別,命名為『WeatherInfo』,把瀏覽器中的JSON檔案整個『全選複製』,點選游標到該類別編輯區的空白處,接著,點選如下圖的功能

以下這部分不需要,刪除

Public Class Rootobject
    Public Property Property1() As Class1
End Class

以下這部分,是我們要的,我們把他的類別名稱換成我們要的,順便把『DataCreationDate』的資料型別改成日期『Date』,最後為每個屬性加上簡單的說明後,變成如下:

Imports Microsoft.VisualBasic

Public Class WeatherInfo
    ''' <summary>
    ''' 地點名稱
    ''' </summary>
    Public Property SiteName As String

    ''' <summary>
    ''' 風向
    ''' </summary>
    Public Property WindDirection As String
    ''' <summary>
    ''' 風力
    ''' </summary>
    Public Property WindPower As String

    Public Property Gust As String

    ''' <summary>
    ''' 能見度
    ''' </summary>
    ''' <returns></returns>
    Public Property Visibility As String

    ''' <summary>
    ''' 溫度
    ''' </summary>
    ''' <returns></returns>
    Public Property Temperature As String

    ''' <summary>
    ''' 濕度
    ''' </summary>
    ''' <returns></returns>
    Public Property Moisture As String

    ''' <summary>
    ''' 氣壓
    ''' </summary>
    ''' <returns></returns>
    Public Property AtmosphericPressure As String

    ''' <summary>
    ''' 天氣描述
    ''' </summary>
    ''' <returns></returns>
    Public Property Weather As String

    ''' <summary>
    ''' 單日雨量
    ''' </summary>
    ''' <returns></returns>
    Public Property Rainfall1day As String

    ''' <summary>
    ''' 單位
    ''' </summary>
    ''' <returns></returns>
    Public Property Unit As String

    ''' <summary>
    ''' 資料時間
    ''' </summary>
    ''' <returns></returns>
    Public Property DataCreationDate As Date
End Class

取得資料

接下來就安排個按鈕,來取得相關資料,為了讓資料不要頻繁的去取得,我們可以把取得的資料放到Session中,我們新增一個畫面(WebForm)『tWeather.aspx』,並放上一個按鈕,相關程式碼如下:

tWeather.aspx

<asp:Button ID="btnGetData" runat="server" Text="取得資料" />

tWeather.aspx.vb

Imports System.Net.Http
Imports Newtonsoft.Json

Partial Class Weather_tWeather
    Inherits System.Web.UI.Page

    '宣告物件集合,用以存放取得的資料
    Dim oWs As List(Of WeatherInfo)

    Private Sub Weather_tWeather_Load(sender As Object, e As EventArgs) Handles Me.Load

        Me.lblErr.Text = ""
        Me.lblCnt.Text = ""

        '從Session取回物件集合
        oWs = Session("oWs")
        '如果取得的內容是空的,就先增物件集合,並放回Session
        If oWs Is Nothing Then
            oWs = New List(Of WeatherInfo)
            Session("oWs") = oWs
        End If
    End Sub

    Protected Sub btnGetData_Click(sender As Object, e As EventArgs) Handles btnGetData.Click

        '宣告HttpClient
        Dim Client As New HttpClient
        '設定連結的網址
        Dim UriWeather As String = "https://opendata.epa.gov.tw/ws/Data/ATM00698/?$format=json"
        '取得資料,傳給response(HttpResponseMessage)
        Dim response As HttpResponseMessage = Client.GetAsync(UriWeather).Result
        '取得JSON的字串
        Dim jsonWs As String = response.Content.ReadAsStringAsync.Result.ToString
        '透過JsonConvert.DeserializeObject 將 JSON字串 轉成物件集合
        oWs = JsonConvert.DeserializeObject(Of List(Of WeatherInfo))(jsonWs)
        Session("oWs") = oWs
    End Sub
End Class

資料存取的類別

接下來,要處理資料存取的類別,這裡有三個部分要處理,分別是

  1. 取得地點不重複的資料
  2. 依據選擇的地點,傳回符合地點的相關物件集合
  3. 符合地點最新(日期最大)的物件

首先,為了傳回不重複地點,提供下拉選單做為資料來源,我們先新增一個地點的類別,用來回傳結果

Public Class WSiteNameInfo
    Public Property SiteName As String = ""
End Class

這個把他與WeatherInfo放一起,相同檔案裡面即可。

來在,在App_Code(或者 Models)撰寫資料存取類別裡面的內容如下:

Imports Microsoft.VisualBasic

Public Class WeatherDao
    ''' <summary>
    ''' 取得地點不重複的資料並傳回
    ''' </summary>
    ''' <returns></returns>
    Public Function GetDistinctSiteName() As List(Of WSiteNameInfo)
        Try
            '宣告回傳的地點物件集合
            Dim oSNs As New List(Of WSiteNameInfo)
            '宣告並從Session取得天氣物件集合
            Dim oWs As List(Of WeatherInfo) = HttpContext.Current.Session("oWs")
            '如果不是空的
            If oWs IsNot Nothing Then
                '而且有值
                If oWs.Count > 0 Then
                    '透過LINQ,取得地點不重複的內容
                    Dim sRlt = From oW As WeatherInfo In oWs
                               Select oW.SiteName Distinct

                    '宣告地點物件
                    Dim tWN As WSiteNameInfo
                    '取得結果逐一讀取出,新增地點物件,並放入回傳的地點物件集合中
                    For Each s As String In sRlt
                        tWN = New WSiteNameInfo
                        tWN.SiteName = s
                        oSNs.Add(tWN)
                    Next
                End If
            End If
            '回傳地點物件集合
            Return oSNs
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    ''' <summary>
    ''' 依據傳入的地點,取得相關資料,並傳回符合的物件集合
    ''' </summary>
    ''' <param name="SiteName">地點</param>
    ''' <returns></returns>
    Public Function GetWeatherBySiteName(ByVal SiteName As String) As List(Of WeatherInfo)
        Try
            '宣告條件撈取結果的物件集合
            Dim oRlts As New List(Of WeatherInfo)
            '宣告並從Session取得天氣物件集合
            Dim oWs As List(Of WeatherInfo) = HttpContext.Current.Session("oWs")

            '如果不是空的
            If oWs IsNot Nothing Then
                '如果傳入的地點有資料
                If SiteName <> "" Then
                    '透過LINQ從物件集合撈取符合地點的資料,並轉成LIST集合物件,放入回傳的物件集合
                    oRlts = (From oW As WeatherInfo In oWs
                             Where oW.SiteName = SiteName
                             Select oW).ToList()
                Else
                    '沒有傳入地點,結果就等於是天氣物件集合的全部
                    oRlts = oWs
                End If
            End If

            '回傳結果
            Return oRlts
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    ''' <summary>
    ''' 依據傳入的地點,取得該地點,資料時間最大的內容,並傳回物件
    ''' </summary>
    ''' <param name="SiteName">地點</param>
    ''' <returns></returns>
    Public Function GetMaxWeatherBySiteName(ByVal SiteName As String) As WeatherInfo
        Try
            '宣告撈取的結果物件集合
            Dim oRlts As List(Of WeatherInfo)
            '宣告並從Session取得天氣物件集合
            Dim oWs As List(Of WeatherInfo) = HttpContext.Current.Session("oWs")
            '宣告回傳的物件
            Dim oRlt As New WeatherInfo
            '如果天氣集合不是空的
            If oWs IsNot Nothing Then
                '地點資料不是空的
                If SiteName <> "" Then
                    '撈取結果依據地點篩選,並依據日期由大至小排序
                    oRlts = (
                            From oW As WeatherInfo In oWs
                            Where oW.SiteName = SiteName
                            Select oW
                            Order By oW.DataCreationDate Descending
                                 ).ToList()
                    '回傳的結果是撈取結果集合的第一筆(最大)
                    oRlt = oRlts.First()
                End If
            End If

            '回傳結果
            Return oRlt
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

End Class


到時候畫面中,就可以用『ObjectDataSource』來應用這個Dao的類別,進行資料讀取與篩選,資料是物件的集合『List (Of Object)』,小喵覺得最方便讀取篩選的方式,就是透過『LINQ』來處理,好懂、好寫、好維護。

再來,就是把畫面中,下拉選單(DropDownList)、符合資料的呈現(GridView)、以及下拉選單選擇地點的最新資料,做為安排。

aspx

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="tWeather.aspx.vb" Inherits="Weather_tWeather" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Label ID="lblErr" runat="server" Text=""></asp:Label>
            <hr />
            <asp:Button ID="btnGetData" runat="server" Text="取得資料" />
            <hr />
            資料總筆數:<asp:Label ID="lblCnt" runat="server" Text=""></asp:Label><br />
            地區:
            <asp:DropDownList ID="ddlSiteName" runat="server" DataSourceID="odsSiteName" DataTextField="SiteName" DataValueField="SiteName" AutoPostBack="True">
            </asp:DropDownList>
            <asp:ObjectDataSource ID="odsSiteName" runat="server" SelectMethod="GetDistinctSiteName" TypeName="WeatherDao">
            </asp:ObjectDataSource>
            <br />
            <div id="divRecently" runat="server">
                <hr />
                最新天氣狀況:
                <br />地點:<asp:Label ID="lblSiteName" runat="server" Text=""></asp:Label>
                <br />風向:<asp:Label ID="lblWindDirection" runat="server" Text=""></asp:Label>
                <br />風力:<asp:Label ID="lblWindPower" runat="server" Text=""></asp:Label>
                <br />可見度:<asp:Label ID="lblVisibility" runat="server" Text=""></asp:Label>
                <br />溫度:<asp:Label ID="lblTemperature" runat="server" Text=""></asp:Label>
                <br />濕度:<asp:Label ID="lblMoisture" runat="server" Text=""></asp:Label>
                <br />氣壓:<asp:Label ID="lblAtmosphericPressure" runat="server" Text=""></asp:Label>
                <br />天氣描述:<asp:Label ID="lblWeather" runat="server" Text=""></asp:Label>
                <br />單日雨量:<asp:Label ID="lblRainfall1day" runat="server" Text=""></asp:Label>
                <br />資料時間:<asp:Label ID="lblDataCreationDate" runat="server" Text=""></asp:Label>
                <hr />
            </div>
            
            <asp:GridView ID="gvWs" runat="server" DataSourceID="odsWs" AutoGenerateColumns="False">
                <Columns>
                    <asp:BoundField DataField="SiteName" HeaderText="地點" SortExpression="SiteName" />
                    <asp:BoundField DataField="WindDirection" HeaderText="風向" SortExpression="WindDirection" />
                    <asp:BoundField DataField="WindPower" HeaderText="風力" SortExpression="WindPower" />
                    <asp:BoundField DataField="Visibility" HeaderText="可見度" SortExpression="Visibility" />
                    <asp:BoundField DataField="Temperature" HeaderText="溫度" SortExpression="Temperature" />
                    <asp:BoundField DataField="Moisture" HeaderText="濕度" SortExpression="Moisture" />
                    <asp:BoundField DataField="AtmosphericPressure" HeaderText="氣壓" SortExpression="AtmosphericPressure" />
                    <asp:BoundField DataField="Weather" HeaderText="天氣描述" SortExpression="Weather" />
                    <asp:BoundField DataField="Rainfall1day" HeaderText="單日雨量" SortExpression="Rainfall1day" />
                    <asp:BoundField DataField="Unit" HeaderText="資料單位" SortExpression="Unit" />
                    <asp:BoundField DataField="DataCreationDate" DataFormatString="{0:yyyy/MM/dd HH:mm:ss}" HeaderText="資料時間" SortExpression="DataCreationDate" />
                </Columns>
            </asp:GridView>
            <asp:ObjectDataSource ID="odsWs" runat="server" SelectMethod="GetWeatherBySiteName" TypeName="WeatherDao">
                <SelectParameters>
                    <asp:ControlParameter ControlID="ddlSiteName" Name="SiteName" PropertyName="SelectedValue" Type="String" />
                </SelectParameters>
            </asp:ObjectDataSource>
            <br />
        </div>
    </form>
</body>
</html>

還有CodeFile的相關內容

tWeather.aspx.vb

Imports System.Net.Http
Imports Newtonsoft.Json

Partial Class Weather_tWeather
    Inherits System.Web.UI.Page

    '宣告物件集合,用以存放取得的資料
    Dim oWs As List(Of WeatherInfo)

    Protected Sub btnGetData_Click(sender As Object, e As EventArgs) Handles btnGetData.Click

        '宣告HttpClient
        Dim Client As New HttpClient
        '設定連結的網址
        Dim UriWeather As String = "https://opendata.epa.gov.tw/ws/Data/ATM00698/?$format=json"
        '取得資料,傳給response(HttpResponseMessage)
        Dim response As HttpResponseMessage = Client.GetAsync(UriWeather).Result
        '取得JSON的字串
        Dim jsonWs As String = response.Content.ReadAsStringAsync.Result.ToString
        '透過JsonConvert.DeserializeObject 將 JSON字串 轉成物件集合
        oWs = JsonConvert.DeserializeObject(Of List(Of WeatherInfo))(jsonWs)
        Session("oWs") = oWs
        BindData()


    End Sub

    Private Sub Weather_tWeather_Load(sender As Object, e As EventArgs) Handles Me.Load

        Me.lblErr.Text = ""
        Me.lblCnt.Text = ""

        '從Session取回物件集合
        oWs = Session("oWs")
        '如果取得的內容是空的,就先增物件集合,並放回Session
        If oWs Is Nothing Then
            oWs = New List(Of WeatherInfo)
            Session("oWs") = oWs
        End If
    End Sub


    Private Sub BindData()
        Me.lblCnt.Text = oWs.Count.ToString()
        Me.ddlSiteName.DataBind()
        Me.gvWs.DataBind()
        divRecentlyDataBind()
    End Sub

    Private Sub divRecentlyDataBind()
        Dim dao As New WeatherDao
        Dim oW As WeatherInfo = dao.GetMaxWeatherBySiteName(Me.ddlSiteName.SelectedValue)
        If oW.SiteName <> "" Then
            divRecentlyInit()
            lblAtmosphericPressure.Text = oW.AtmosphericPressure
            lblDataCreationDate.Text = oW.DataCreationDate
            lblMoisture.Text = oW.Moisture
            lblRainfall1day.Text = oW.Rainfall1day
            lblSiteName.Text = oW.SiteName
            lblTemperature.Text = oW.Temperature
            lblVisibility.Text = oW.Visibility
            lblWeather.Text = oW.Weather
            lblWindDirection.Text = oW.WindDirection
            lblWindPower.Text = oW.WindPower
        End If
    End Sub

    Private Sub odsSiteName_Selected(sender As Object, e As ObjectDataSourceStatusEventArgs) Handles odsSiteName.Selected
        Dim tLI As New ListItem("請選擇", "")
        Me.ddlSiteName.Items.Insert(0, tLI)
    End Sub

    Private Sub divRecentlyInit()
        Me.lblAtmosphericPressure.Text = ""
        Me.lblDataCreationDate.Text = ""
        Me.lblMoisture.Text = ""
        Me.lblRainfall1day.Text = ""
        Me.lblSiteName.Text = ""
        Me.lblTemperature.Text = ""
        Me.lblVisibility.Text = ""
        Me.lblWeather.Text = ""
        Me.lblWindDirection.Text = ""
        Me.lblWindPower.Text = ""
    End Sub

    Private Sub ddlSiteName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ddlSiteName.SelectedIndexChanged
        divRecentlyDataBind()
    End Sub
End Class

得到的結果如下圖:

末記

回顧一下此篇有三大重點:

  1. 如何從公開資料(JSON)取得資料
  2. 如何將JSON資料轉類別
  3. 如何透過LINQ針對物件集合進行資料讀取篩選

提供大家與未來的小喵參考

相關程式碼,請參考以下:

https://github.com/topcattw/WebFormGetOpenDataJSON

^_^

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6) 
topcat
Blog:http://www.dotblogs.com.tw/topcat