WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Settings.settings の使い方

by WebSurfer 2021年6月5日 15:09

.NET Framework アプリで利用できる Settings.settings からの情報の取得方法と、アプリからの情報の設定方法を備忘録として書いておきます。

Settings.settings

例えば Windows Forms アプリでウィンドウの表示位置とサイズを設定するため、上の画像のように Settings.settings に値を格納したとします。

それを以下のコードのように利用できます。

Form1.cs のコード

初回にユーザーがアプリを立ち上げた時、Settings.settings で指定した位置とサイズでフォームが表示されるよう、コンストラクタ Form1 で構成ファイルから値を取得して位置とサイズを設定しています。

その後、ユーザーが表示位置とサイズを変更した場合、次回アプリを立ち上げた時に変更した位置とサイズで表示できるよう、Form をクローズするときのイベントハンドラ Form1_FormClosing で位置とサイズ情報を構成ファイルに書き込んで保存しています。

以上で本題の話は終わりなのですが、その時使われる構成ファイルがどうなっているかについても調べましたので、オマケで書いておきます。

基本的には先の記事「構成ファイルの保存場所」に書いた通りです。それを画像を使ってもう少し詳しく書きます。

一番上の画像のように Settings.settings に値を格納すると、それらは自動的に App.config に反映されます。下の画像を見てください。

App.config

同時に Settings.Designer.cs に、Settings.settings に設定した Name と同じ名前でプロパティが設定され、Value の値がデフォルト値として設定されます。

Settings.Designer.cs

Settings.settings の Type に設定したとおりプロパティは int に型付けられていることに注意してください。型付けられていることが Settings.settings を使うことのメリットでもあります。Scope を User にするとプロパティには setter も含まれるようになり、プログラムから値を変更することができます(Scope が Application の場合は getter のみ)。

アプリをビルドすると、生成される .exe ファイルと同じ場所に、App.config の内容をそのままコピーして xxxxx.exe.config という名前のファイルが作られます。(xxxxx はアプリケーション名。Visual Studio のバージョンによってはデバッグ用はデフォルトで xxxxx.vshost.exe.config と異なるので注意してください)

xxxxx.exe.config

アプリを起動すると App.config からではなく、上記の構成ファイルから情報を取得し、それに指定されている位置とサイズでフォームを表示します。ただし、それは初回だけです。

上のコードの画像を見てください。フォームをクローズすると、イベントハンドラ Form1_FormClosing でクローズ時点での位置とサイズ情報を保存するようにしています。そのとき、user.config という構成ファイルを新たに作って情報を書き込みます。(App.config でも xxxxx.exe.config でもない点に注意してください)

user.config

2 回目以降アプリを立ち上げた際は user.config から位置とサイズ情報を取得してフォームを表示します。ユーザーが位置とサイズを変更すればフォームがクローズされる都度その時点での位置とサイズ情報が user.config に書き込まれます。

というわけで、.NET Framework のアプリでは以上の仕組みでユーザーが設定した情報が保持されるようになっているようです。

なお、.NET Core 3.1 の WinForms アプリでも、自分が試した限りですが、上記とほぼ同様に Settings.settings を利用できました。ただし、user.config は .NET Framework 版アプリと同様に生成されるものの、その他の構成ファイルがどのようになっているのかが調べ切れていません。今後の課題ということで・・・

Tags:

.NET Framework

JSON 文字列から指定した name の value を取得

by WebSurfer 2021年2月11日 18:12

JSON 形式の文字列 {"name":"value"} から name を指定して value を取得する C# のプログラムのサンプルを書きます。

JSON 文字列から指定した name の value を取得

先の記事「Json.NET の JToken をパース」と「System.Text.Json の JsonElement をパース」で、Json.NET (Newtonsoft.Json) と System.Text.Json を使って JSON 文字列をパースするサンプルを書きました。

先の記事のサンプルの使い道はなさそうですが、その応用でいろいろできそうということで、まず JSON 文字列を JToken (Json.NET) または JsonElement (System.Text.Json) にデシリアライズしたオブジェクトから、name を指定して再帰的に value 値を取得するコードを書いてみました。

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

レアケースかもしれませんけど、ASP.NET Forum でそういう話があったので考えてみました。

JToken (Json.NET) および JsonElement (System.Text.Json) の両方の場合のサンプルコードを以下にアップしておきます。

(1) JToken (Json.NET)

Json.NET (Newtonsoft.Json) の DeserializeObject メソッドで JSON 文字列を JToken クラスのオブジェクトにデシリアライズし、その中から name が "ObjectArray" の value 値を FindJTokenByName メソッドで取得しています。

その結果を Visual Studio 2019 のデバッガで見たのが上の画像です。元になる JSON 文字列はこの記事の下の方に書いてありますのでそちらを見てください。

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;

namespace NewtonsoftJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Users\...\NewtonsoftJson\";
            string file = "TextFile5.txt";
            string jsonText = "";
            using (StreamReader sr = File.OpenText(path + file))
            {
                jsonText = sr.ReadToEnd();
            }

            JToken jtoken = JsonConvert.DeserializeObject<JToken>(jsonText);
            Parse(0, jtoken);
            JToken token = FindJTokenByName(jtoken, "ObjectArray");
        }

        private static void Parse(int padding, JToken jtoken)
        {
            // ・・・略・・・
        }

        private static JToken FindJTokenByName(JToken jtoken, string name)
        {
            if (jtoken is JObject)
            {
                foreach (KeyValuePair<string, JToken> kvp in (JObject)jtoken)
                {
                    if (kvp.Key == name)
                    {
                        return kvp.Value;
                    }
                    else
                    {
                        JToken retVal = FindJTokenByName(kvp.Value, name);
                        if (retVal != null)
                        {
                            return retVal;
                        }
                    }
                }
            }
            else if (jtoken is JArray)
            {
                foreach (JToken jtokenInArray in (JArray)jtoken)
                {
                    JToken retVal = FindJTokenByName(jtokenInArray, name);
                    if (retVal != null)
                    {
                        return retVal;
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }
    }
}

(2) JsonElement (System.Text.Json)

System.Text.Json の Deserialize メソッドで JSON 文字列を JsonElement 構造体のオブジェクトにデシリアライズし、その中から name が "ObjectArray" の value 値を FindElementByName メソッドで取得しています。

using System;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.IO;

namespace ConsoleAppJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Users\surfe...\ConsoleAppJson\";
            string file = "TextFile5.txt";
            string jsonText = "";

            using (StreamReader sr = File.OpenText(path + file))
            {
                jsonText = sr.ReadToEnd();
            }

            JsonElement jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonText);
            Parse(0, jsonElement);
            var jelem = FindElementByName(jsonElement, "ObjectArray");
        }

        private static void Parse(int padding, JsonElement jelem)
        {
            // ・・・略・・・
        }

        // JsonElement は構造体。見つからないと null が返ってくるので
        // 戻り値は JsonElement? として null 許容にした
        private static JsonElement? FindElementByName(JsonElement jelem, string name)
        {
            if (jelem.ValueKind == JsonValueKind.Object)
            {
                foreach (JsonProperty jprop in jelem.EnumerateObject())
                {
                    if (jprop.Name == name)
                    {
                        return jprop.Value;
                    }
                    else
                    {
                        JsonElement? retVal = FindElementByName(jprop.Value, name);
                        if (retVal != null)
                        {
                            return retVal;
                        }
                    }
                }
            }
            else if (jelem.ValueKind == JsonValueKind.Array)
            {
                foreach (JsonElement jelemInArray in jelem.EnumerateArray())
                {
                    JsonElement? retVal = FindElementByName(jelemInArray, name);
                    if (retVal != null)
                    {
                        return retVal;
                    }
                }
            }
            else
            {
                return null;
            }
            return null;
        }
    }
}

上のコードで使った TextFile5.txt の JSON 文字列は以下の通りです。

{
  "Title": "This is my title",
  "Response": {
    "Version": 1,
    "StatusCode": "OK",
    "Result": {
      "Profile": {
        "UserName": "SampleUser",  
        "IsMobileNumberVerified": false,
        "MobilePhoneNumber": null
      },
      "ObjectArray" : [
          {"Code": 2000,"Description": "Fail"},
          {"Code": 3000,"Description": "Success"}
      ],
      "lstEnrollment": "2021-2-5"
    },
    "Message": {
      "Code": 1000,      
      "Description": "OK"
    }
  },
  "StringArray" : ["abc", "def", "ghi"]
}

Tags: , ,

.NET Framework

Json.NET の JToken をパース

by WebSurfer 2021年2月7日 15:04

Json.NET (Newtonsoft.Json) の JsonConvert.DeserializeObject メソッドを使って JSON 文字列を JToken 型のオブジェクトにデシリアライズし、それを以下のようにパースして表示する方法を書きます。

JToken をパース

使用するのは Newtonsoft.Json.Linq 名前空間にある JToken, JObject, JArray, JValue クラスですので、まずそれらの簡単な説明を以下に書きます。詳しい説明は Newtonsoft.Json.Linq Namespace を見てください。

  • JToken: 下のクラスの継承元の抽象クラス
  • JObject: {"name1":"value1","name2":"value2"} のようなオブジェクト。IDictionary<string, JToken> を継承
  • JArray: [{"name":"value1"},{"name":"value2"}, ...] のような配列。IList<JToken> を継承
  • JValue: string, number, boolean, null などのプリミティブな JSON 値

一般的な方法は DeserializeObject<T>(String) メソッドの T に先の記事「JSON 文字列から C# のクラス定義生成」のような方法で求めたクラス定義を設定して、そのクラスのオブジェクトにデシリアライズするのだと思います。

なので、この記事に書くような JToken クラスにデシリアライズしてそれをパースする必要はなさそうですが、ひょっとしたら JSON 文字列を受け取るまでその内容が不明でクラス定義が生成できないとかいうケースがあるかもしれないということで考えてみました。

そのコードを以下にアップしておきます。

DeserializeObject<JToken>(jsonText) で JSON 文字列を JToken 型のオブジェクトにデシリアライズします。その後 Parse メソッドで JToken オブジェクトを再帰的に解析し結果をコンソールに出力したのが上の画像です。

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;

namespace NewtonsoftJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"C:\Users\...\NewtonsoftJson\";
            string file = "TextFile5.txt";

            string jsonText = "";

            file = "TextFile5.txt";
            using (StreamReader sr = File.OpenText(path + file))
            {
                jsonText = sr.ReadToEnd();
            }

            JToken jtoken = JsonConvert.DeserializeObject<JToken>(jsonText);
            Parse(0, jtoken);

        }

        private static void Parse(int padding, JToken jtoken)
        {
            if (jtoken is JValue)
            {
                // プリミティブ型の場合 Value プロパティで値を取得
                JValue jvalue = (JValue)jtoken;
                string str = $"value = {jvalue.Value}";
                Console.WriteLine(str.PadLeft(str.Length + padding));
            }
            else if (jtoken is JObject)
            {
                // JObject は IDictionary<string, JToken> を継承しているので、
                // 以下のように foreach ループで {"name":"value"} を取得できる
                foreach (KeyValuePair<string, JToken> kvp in (JObject)jtoken)
                {
                    if (kvp.Value is JValue)
                    {
                        JValue jvalue = (JValue)kvp.Value;
                        string str = $"name = {kvp.Key}, value = {jvalue.Value}";
                        Console.WriteLine(str.PadLeft(str.Length + padding));
                    }
                    else if (kvp.Value is JObject)
                    {
                        string str = $"name = {kvp.Key}";
                        Console.WriteLine(str.PadLeft(str.Length + padding));
                        Parse(padding + 2, kvp.Value);
                    }
                    else if (kvp.Value is JArray)
                    {
                        string str = $"name = {kvp.Key}";
                        Console.WriteLine(str.PadLeft(str.Length + padding));
                        JArray jarray = (JArray)kvp.Value;
                        int index = 1;

                        // JArray は IList<JToken> を継承しているので、以下の
                        // ように foreach で配列の各要素の JToken を取得できる
                        foreach (JToken token in jarray)
                        {
                            string idx = $"array index {index}";
                            Console.WriteLine(idx.PadLeft(idx.Length + padding + 1));
                            Parse(padding + 2, token);
                            index++;
                        }
                    }
                }
            }
            else if (jtoken is JArray)
            {
                JArray jarray = (JArray)jtoken;
                int index = 1;
                foreach (JToken token in jarray)
                {
                    string str = $"array index {index}";
                    Console.WriteLine(str.PadLeft(str.Length + padding + 1));
                    Parse(padding + 2, token);
                    index++;
                }
            }
            else
            {
                // ここには来ないはずだが念のため
                Console.WriteLine(jtoken.ToString());
            }
        }
    }
}

ちなみに、上のコードで使った JSON 文字列は以下の通りです。他にもいろいろ試してみましたが、JSON 文字列としてはかなり無理目でクラス定義が生成できない場合も上のコードでパースは可能でした。ダメなケースもあるかもしれませんが・・・

{
  "Title": "This is my title",
  "Response": {
    "Version": 1,
    "StatusCode": "OK",
    "Result": {
      "Profile": {
        "UserName": "SampleUser",  
        "IsMobileNumberVerified": false,
        "MobilePhoneNumber": null
      },
      "ObjectArray" : [
          {"Code": 2000,"Description": "Fail"},
          {"Code": 3000,"Description": "Success"}
      ],
      "lstEnrollment": "2021-2-5"
    },
    "Message": {
      "Code": 1000,      
      "Description": "OK"
    }
  },
  "StringArray" : ["abc", "def", "ghi"]
}

Tags: , , , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar