WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

JSON 文字列の日付時刻のデシリアライズ

by WebSurfer 18. February 2022 15:49

日付時刻を表す JSON 文字列を .NET の DateTime 型のオブジェクトにデシリアライズするにはどのようにするかという話を書きます。

{"name":"value"} という JSON 文字列の value に直接設定できるのは string, number, object, array, true, false, null だけですので、.NET の DateTime オブジェクトは string 型に変換されて JSON 文字列に設定されます。

変換の結果はシリアライザによって違います。詳しくは先の記事「日付時刻と JSON 文字列」に書きましたが、それをまとめた表を以下に再掲しておきます。表の一番最後の項目 (6) ASP.NET Web API は Newtonsoft.Json および System.Text.Json 名前空間の JsonSerializer クラスを使ったシリアライズ結果です。

No. 方法 結果
(1) JSON.stringify() 2017-02-01T03:15:45.000Z
(2) DataContractJsonSerializer \/Date(1503727168573+0900)\/
(3) WCF 上記 (2) の結果と同じ
(4) JavaScriptSerializer \/Date(1030806000000)\/
(5) ASP.NET Web サービス 上記 (4) の結果と同じ
(6) ASP.NET Web API 2017-08-26T15:39:32.6330349+09:00

上の表のように string 型にシリアライズされた JSON 文字列を元の DateTime 型のオブジェクトに変換するには、自力でコードを書いて文字列をパースする必要があると思っていました。

しかしそれは思い違いで、上の表の形式にシリアライズした文字列は Newtonsoft.Json であれば全て、System.Text.Json 名前空間の JsonSerializer クラスの場合も (1) と (6) は DateTime 型にデシリアライズしてくれました。

知ってましたか? 実は自分は最近まで知らなかったです。(汗) 以下にその話を書きます。

まず、JSON 文字列を C# のオブジェクトにデシリアライズする際、C# のオブジェクトのクラス定義が必要ですが、Visual Studio のツールを利用してそれが可能です。詳しくは先の記事「JSON 文字列から C# のクラス定義生成」を見てください。

Visual Studio のツールを使って C# のクラス定義を生成すると、上の表の日付日時の JSON 文字列に該当するプロパティの型は、上の表の例 (1) ~ (6) 全てが DateTime 型になります。

例えば、以下のような JSON 文字列から Visual Studio のツールを使って C# のクラス定義を生成すると、

{
  "name":"WebSurfer",
  "date":"2017-02-01T03:15:45.000Z"
}

日付時間の JSON 文字列に該当するプロパティの型は以下のよう DataTime 型になります。

public class Rootobject
{
    public string name { get; set; }
    public DateTime date { get; set; }
}

シリアライザによって JSON 文字列の形式が上の表の (1) ~ (6) のように異なりますが、いずれも同じ結果すなわち C# のクラスの当該プロパティの型は DateTime になります。

次にデシリアライザが上の表の (1) ~ (6) の文字列に対応しているかを調べました。結果は上にも述べましたが Newtonsoft.Json は (1) ~ (6) 全てに対応していました。検証に使ったコードを以下に載せておきます。

using Newtonsoft.Json;
using System;

namespace ConsoleNewtonsoftJson
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string jsonString1 = "{\"name\":\"WebSurfer\",\"date\":\"2017-02-01T03:15:45.000Z\"}";
            string jsonString2 = "{\"name\":\"WebSurfer\",\"date\":\"\\/Date(1503727168573+0900)\\/\"}";
            string jsonString3 = "{\"name\":\"WebSurfer\",\"date\":\"\\/Date(1030806000000)\\/\"}";
            string jsonString4 = "{\"name\":\"WebSurfer\",\"date\":\"2017-08-26T15:39:32.6330349+09:00\"}";

            Rootobject rootobject = JsonConvert.DeserializeObject<Rootobject>(jsonString1);
            Console.WriteLine($"name: {rootobject.name}, date: {rootobject.date}");

            rootobject = JsonConvert.DeserializeObject<Rootobject>(jsonString2);
            Console.WriteLine($"name: {rootobject.name}, date: {rootobject.date}");

            rootobject = JsonConvert.DeserializeObject<Rootobject>(jsonString3);
            Console.WriteLine($"name: {rootobject.name}, date: {rootobject.date}");

            rootobject = JsonConvert.DeserializeObject<Rootobject>(jsonString4);
            Console.WriteLine($"name: {rootobject.name}, date: {rootobject.date}");
        }
    }

    public class Rootobject
    {
        public string name { get; set; }
        public DateTime date { get; set; }
    }
}

/*
実行結果は:
name: WebSurfer, date: 2017/02/01 3:15:45
name: WebSurfer, date: 2017/08/26 14:59:28
name: WebSurfer, date: 2002/08/31 15:00:00
name: WebSurfer, date: 2017/08/26 15:39:32
*/

JsonSerializer クラスのデシリアライザも同様に検証してみましたが、jsonString2 と jsonString3 場合は JsonException がスローされ "The JSON value could not be converted to System.DateTime." というエラーメッセージが出ます。jsonString1 と jsonString4 は問題なく DateTime 型のオブジェクトにデシリアライズされました。

例外のスタックトレースを見ると、Utf8JsonReader.GetDateTime() メソッドで例外がスローされており、そのメソッドの Microsoft のドキュメントを見ると Remarks に "This method only creates a DateTime representation of JSON strings that conform to the ISO 8601-1 extended format" と書いてあります。

ということで、上の表の (1) と (6) の文字列は ISO 8601-1 extended format に準拠しているので DateTime にデシリアライズできた、他の形式はダメだったということのようです。

Tags: , ,

.NET Framework

WPF で Chart を表示

by WebSurfer 8. February 2022 12:33

ASP.NET Web アプリで Chart を使う方法を先の記事「Chart (ASP.NET Web Forms 用)」や「MVC で Chart を利用する方法」に書きましたが、それらに表示した棒グラフを同様に Chart を使って WPF アプリで表示する方法を書きます。

WPF アプリに Chart 表示

この記事でプロジェクトの作成に使ったのは Visual Studio Community 2022 のテンプレート「WPF アプリ (.NET Framework)」で、フレームワークは .NET Framework 4.8 です。

プロジェクトを作成したら、ソリューションエクスプローラーの「参照」を左クリック ⇒[参照の追加(R)...]をクリックして表示される「参照マネージャー」」ダイアログで以下の項目を参照に追加します。

  • WindowsFormIntegration
  • System.Windows.Forms
  • System.Windows.Forms.DataVisualization

上の 2 つが WPF アプリで Windows Forms 用のコントロールを使うために必要なもの、最後のものが Chart コントロールを使うためのものです。

次に、自動生成された MainWindow.xaml の XAML のコードに以下の画像の赤枠のコードを追加します。赤枠部分に Windows Froms 用のコントロール(この記事の例では Chart)を配置することができます。

XAML

Microsoft のドキュメント「チュートリアル: WPF での XAML を使用した Windows フォーム コントロールのホスト」のように XAML 上で Windows Forms 用コントロールを配置することができますが、この記事では MainWindow.xaml.cs で動的に Chart コントロールを作って配置することにします。

MainWindow.xaml.cs のコードは以下の通りです。これを実行した結果がこの記事の一番上の画像です。

using System.Windows;
using System.Data;
using System.Windows.Forms.DataVisualization.Charting;
using System.Data.SqlClient;

namespace WpfChart
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly Chart chart;

        public MainWindow()
        {
            InitializeComponent();

            this.chart = new Chart
            {
                Name = "chart1",
                Width = 600,
                DataSource = CreateDataTable().DefaultView
            };

            this.windowsFormsHost.Child = this.chart;

            Legend legend = new Legend()
            {
                DockedToChartArea = "ChartArea1",
                IsDockedInsideChartArea = false,
                Name = "Legend1"
            };

            chart.Legends.Add(legend);

            Series series = new Series()
            {
                Name = "三吉",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "三吉"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "春日",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "春日"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "東雲",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "東雲"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "府中",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "府中"
            };
            chart.Series.Add(series);

            series = new Series()
            {
                Name = "広島",
                ChartType = SeriesChartType.StackedColumn,
                CustomProperties = "DrawingStyle=Cylinder",
                IsValueShownAsLabel = true,
                Label = "#PERCENT{P1}",
                Legend = "Legend1",
                XValueMember = "Month",
                YValueMembers = "広島"
            };
            chart.Series.Add(series);

            ChartArea chartArea = new ChartArea()
            {
                Name = "ChartArea1",
                AxisY = new Axis() { Title = "売上高" },
                AxisX = new Axis() { Title = "売上月" }
            };
            chart.ChartAreas.Add(chartArea);
        }

        private DataTable CreateDataTable()
        {
            string connString = @"・・・接続文字列・・・";
            string selectQuery = @"SELECT Month, 
                SUM(CASE WHEN Name = N'三吉' THEN Sales ELSE 0 END) AS 三吉,
                SUM(CASE WHEN Name = N'春日' THEN Sales ELSE 0 END) AS 春日,
                SUM(CASE WHEN Name = N'東雲' THEN Sales ELSE 0 END) AS 東雲,
                SUM(CASE WHEN Name = N'府中' THEN Sales ELSE 0 END) AS 府中,
                SUM(CASE WHEN Name = N'広島' THEN Sales ELSE 0 END) AS 広島
                FROM Shop GROUP BY Month";

            using (var connection = new SqlConnection(connString))
            {
                using (var command = new SqlCommand(selectQuery, connection))
                {
                    var adapter = new SqlDataAdapter(command);
                    var table = new DataTable();
                    adapter.Fill(table);
                    return table;
                }
            }
        }
    }
}

Tags: ,

.NET Framework

Windows Forms 用 Chart Samples

by WebSurfer 30. November 2021 14:28

Chart Samples は Microsoft のサイトから Windows Forms 用と ASP.NET Web Forms 用の両方を入手できたのですが、そのページは現在リンク切れとなっており復活しそうもありません。Windows Forms アプリ用だけは別の入手先を見つけましたので、それを以下に書いておきます。

Windows Forms 用 Chart Samples

Windows Forms 用 Chart Samples は 2021/11/30 現在以下の GitHub のページからダウンロードできます。

Microsoft Chart for Windows Forms Samples Environment

上のページの右の方にある Latest というリンクをクリックするとダウンロードページに遷移しますので、そこで[Source code (zip)]をクリックすれば Samples-Environments-for-Microsoft-Chart-Controls-1.0.1.zip という名前の .zip ファイルがダウンロードされます。

ダウンロードした .zip ファイル

その zip ファイルの中に src というフォルダがあってそれに完全な Windows Forms アプリのソリューションとしてサンプルが含まれています。

それを適当なフォルダに解凍し、Visual Studio で WinFormsChartSamples.sln を開いて実行すれば、基本の解説、いろいろなタイプのサンプルのデモ、それを作るための C# および VB.NET サンプルコード等が満載の Windows Forms アプリが動くはずです。それがこの記事の一番上の画像です。

GitHub のページには "This is a backup of the Microsoft Chart for Windows Forms Samples Environment (Microsoft Chart Controls for Microsoft .NET Framework 3.5) ..." という記述がありますが、Visual Studio 2022 でソリューションを開いて[対象のフレームワーク(G)]みると .NET Framework 4 となってました。(3.5 というのは間違い?)

なお、ビルドする際、プロジェクトルート直下の MainForm.resx と Utilities\SampleMain フォルダの MainForm.resx に対して以下のエラーが出るかもしれません。

"ファイル MainForm.resx を処理できませんでした。インターネットまたは制限付きゾーン内にあるか、ファイルに Web のマークがあるためです。これらのファイルを処理するには、Web のマークを削除してください"

その場合は、エクスプローラーで対象のファイルのプロパティを開き、下の画像の赤枠で示したセキュリティの部分の[許可する(K)]にチェックを入れて[OK]をクリックすれば解決するはずです。

MainForm.resx のプロパティ

Microsoft のサイトからサンプルを入手した場合の設定方法はなどは先の記事は「Chart Samples」に書きました。今となっては役に立たなかもしれませんが、復活するかもしれないので記事は残しておきます。

Tags: , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後ブログ2を追加し、ここはプログラミング関係、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  May 2022  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar