質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • C#

    9215questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • JSON

    1484questions

    JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

C#でjsonファイルを編集したい!(少し?難あり)

受付中

回答 5

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 348

nonki03

score 0

現在3つのアプリを開発中で、それらは全て画面に表示する文言をjson形式にて管理する予定です。
他国でも使用されるアプリのため、その文言管理用jsonファイルは各国の文言を記載したものがそれぞれ用意されることとなります。
バージョンアップにて新しく文言が追加される度、英語を基準に各国の方が翻訳し、その国ごとの文言管理用jsonファイルを作成するための、技術者でない人でも扱えるような文言変更ツールが必要となります。
今回はその文言変更ツールの作成です。

ツールの開発環境はVS2008、Frameworkは3.5で作成しています。
その読み込むjsonファイルの中身が以下のような内容です。


{
"LoginScreen":{
"label_title":"Login Screen",
"label_userId":"User ID",
"label_password":"Password",
"button_login":"LOGIN"
←(増える可能性あり)
},
"UserLIstScreen":{
"label_title":"User List",
"label_userId":"User ID",
"label_userName":"User Name",
"label_userTell":"TEL",
"label_phone":"Phone",
"label_save":"SAVE"
←(増える可能性あり)
},
"SettingListScreen":{
"label_title":"Setting",
"label_storeName":"Store Name",
"label_language":"Language",
"button_save":"SAVE"
←(増える可能性あり)
}
←(更に他画面が増える)
}


画面ごとに区切られていて、その中にメッセージのkeyとvalue といった形になってます。
画面名・メッセージのkey・valueはそれぞれ全て動的な上、今後文言が増えることもありますし、画面が増えることもあります。
その度にツールを修正するようなことはしたくありません。

やりたいこととしては、
1.jsonファイルを読み込み、データをメモリに保持。
2.フォームのDataGridViewに値をセット、画面にて編集できるようにする。
(その際、変更された文言・追加された文言・既存文言が分かるようにする。)
3.保存ボタン押下時に、編集した新しいjsonファイルを排出。
といった内容です。

現在jsonファイルの読み込みの段階で、読み込むには別クラスによる型宣言が必要だと認識しているのですが、キーが動的なゆえに別クラスによる型宣言がうまくできず、どう読み込めばいいのか分からなくて詰まっています。
→なんとか読み込み、切り分け、画面に表示までできました。
ただ、やり方が少し強引というか…

        private void EditScreen_Load(object sender, EventArgs e)
        {


            try
            {
                var jScreen = JObject.Parse(File.ReadAllText("D:\\MessageWork\\En_Old\\risou.json", Encoding.Default));

                foreach (var MenuMsg in jScreen.Properties())
                {
                    MessageBox.Show(Regex.Replace(MenuMsg.ToString(), ".*{", "{"));
                    var dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(Regex.Replace(MenuMsg.ToString(), ".*{", "{"));
                    string sss = "";
                    foreach (var ScreenMsg in dic.Keys)
                    {
                        sss += ScreenMsg.ToString() + "  :  " + dic[ScreenMsg].ToString() + "\r\n";
                    }
                    MessageBox.Show(sss);

                }

            }
            catch (Exception ex)
            {
                string msg = string.Format("Error Occurred! \r\n\r\n{0}", ex.ToString());
                MessageBox.Show(msg);
            }
        }

JObject.Parseを使って画面ごとに切り出し、
切り出した後の左側の部分を無理やりReplaceでカット
JsonConverter.DeserialiseObjectを通して{}の部分を読み込み。
画面に表示…といった感じです。

出来た、としてもいいのですが、あまり綺麗ではないかな…と感じています。
これ以外に「この方法の方が良い」といったような方法は無いでしょうか…

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • gentaro

    2019/06/16 12:57

    XMLを取り込む機能って、それは「XML文書そのもの」を扱っているという話じゃないんですか?
    XML(の表現で)シリアライズされたオブジェクトをJavaのオブジェクトにデシリアライズするのとは意味が異なると思いますが。(後者の話は今回の話と同じ問題が発生するはず)

    キャンセル

  • YAmaGNZ

    2019/06/16 13:12

    Q71さんの回答じゃダメなんですか?

    キャンセル

  • Q71

    2019/06/16 23:11

    今回の用途、メッセージリソースをツールの開発者以外が用意する、というのは、オンラインソフトウェアではよく見られるものです。「形・型が決まっていない」と見るか(こう見たのでdynamicを使う必要があり、4以降じゃないと無理だと思った)、「string,string という対の個数が決まっていない」と見るかで、設計に無理云々は変わると思います。DataGridViewに表示するものの個数なんて、設計時には不明なのですから。
    おっと、フォームの個数も変わるって?コンボボックスで表示するマスタを切り替えるという話も、よくあることです。知っている形に落とし込めれば、簡単になります。

    キャンセル

回答 5

+2

質問欄のやりとりを見て回答を変更します。

最終的にやりたいのは

  1. JSON形式のテキストファイルを読み込む
  2. 読み込んだデータは「画面名-項目名-現地語のテキスト」の形式として編集できるようにする
  3. JSON形式のテキストファイルとして書き出す

という話みたいなので、2の部分は

public class Entry {
    pubilc string ViewName { get; set;}
    pubilc string ItemName { get; set;}
    pubilc string Text { get; set;}
}


みたいなやつをList<Entry>みたいにリストに突っ込んで扱えれば十分だと思います。

問題は1と3ですが、これを無理やりJSONをデシリアライズ・シリアライズでやろうとしているあたりが苦労しているポイントだと思います。

Zuishinさんがコメントされていたとおり、JSONはただのテキストファイルです。
これを読み込んで、上記のクラスのインスタンスが作れたり、書き出せたりすれば十分なはずです。

例えば

public class JSonDataFileReader {
    public IEnumerable<Entry> Load(string filename) {
        var data = File.ReadAllText(filename);
        var result = new List<Entry>();
        // ループ: dataの中身を正規表現を使って1画面分ずつマッチさせる
            var viewname = // 画面名に該当する要素を正規表現で取り出す
            // ループ: マッチさせた1画面分の文字列から、各要素ごとにマッチさせる
                var obj = new Entry {
                    ViewName = viewName,
                    ItemName = // 項目名に該当する要素を正規表現で取り出す
                    Text = // テキストに該当する要素を正規表現で取り出す
                };
                result.Add(obj);

         return result;
    }
}

みたいな感じで読めますよね。(書き出す方は省略)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/16 12:05

    やりたいことはまさしくそうです。
    jsonの中身を取り込んでList型としてメモリに保持、画面で編集して編集後のデータを元のjsonと同じフォーマットで書き出す。これだけです。
    標準のライブラリにも、その他別ライブラリにもjsonを読み込む機能は色々あるのに、正規表現でゴリゴリと切り取ってList型に放り込むのはどうなのかな…と思っていましたが、画面名やKeyの部分が可変であることから、今回の読み込みに関しては正規表現を使ってちゃちゃっと切り取って取り込んだほうがよさそうに思えてきました。

    キャンセル

+2

質問に対しては全然あさっての話でしたなんですけど、ビジネス的に意味ある気がするのでかきますね。

多言語対応をしてるアプリで外注とかもしてるエンジニア側の取りまとめしてました。
翻訳てワードとか文字とかが課金単位です。これで外注すると、たとえばuseridで二重にお金かかります。
お金の問題ならまだましなんですが、それぞれ違った翻訳されることがあって、英語が共通ってわかってても検収のときにチェックされません。
さらに、ログインだけ改修したときにそこだけ翻訳に出します。変わってないのにお金払いたくないから。その翻訳戻りが未改修の既存部分と合致してるかなんて絶対にスルーされます。
そして、真面目なかたが見て直すと、今度は直すべきでないやつを直してしまいます。業務直結でない例だとうまいたとえがないのですが、waterをお湯と水に分けてるのを合わせちゃだめなんです。

意味ベースで単純なキーバリューにしたほうがいいと思います。翻訳は。翻訳はTSVでバージョン管理して、みるときはエクセル、がよいと思います。
そしてそのキーを画面にマップしたほうがいいと思います。ということで要件見直しては?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/16 12:24

    なるほど…翻訳作業を依頼する場合はそのようになるのですね…
    まだ開発で手一杯の駆け出しなので、そういったところに焦点を合わせたことがありませんでした。ご教授ありがとうございます。

    以前の旧アプリのほうでは単純なキーバリューだったのですが、どこの画面の文言かわからないから~とのことで、今回のようなフォーマットになったそうです。
    別の旧アプリのほうでは、[画面コード_キー,バリュー]といった形でCSVにて管理していたため、そういったところを踏まえて見直す気はないか相談を持ち掛けてみます。

    キャンセル

+1

混乱させてしまって申し訳ない。.NET Framework 3.5 で実行できます。
Visual Studio 2019 で、C#, Windows Form アプリケーション, .NET Framework 3.5 で作成したプロジェクト。実行環境に 「lang/en.json」が、 UTF-8 で保存されていることを前提。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Newtonsoft.Json;

namespace teratail194657
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            var resource = LoadResources("en");
        }

        private Dictionary<String, Dictionary<String, String>> LoadResources(String lang)
        {
            String filename = Path.Combine("lang", $"{lang}.json");
            var serializer = new JsonSerializer();
            using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
            using (var reader = new StreamReader(stream, Encoding.UTF8))
            using (var jreader = new JsonTextReader(reader))
            {
                return serializer.Deserialize<Dictionary<String, Dictionary<String, String>>>(jreader);
            }
        }
    }
}


resource に、Dictionary<String, Dictionary<String, String>> の形式で入ってくるので、resource["LoginScreen"]["label_title"] のようにアクセスします。

追記
編集ツールでは、foreeach 句で KeyVakuePair<> にアクセスするなどしてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/13 23:00 編集

    > resource["LoginScreen"]["label_title"] のようにアクセスします。

    画面やその中の項目は増える(減ることも?)そうですので、上記のようなコーディングはできないはずで、その対応が必要かと思います。

    キャンセル

  • 2019/06/14 09:22

    対策?Dictionaryクラスの使い方次第だと思いますが?実際、「その対応」はしていますので。

    キャンセル

  • 2019/06/14 09:44 編集

    > Dictionaryクラスの使い方次第だと思いますが?

    その使い方を書いておかれた方が親切だと思うのですが。

    > resource["LoginScreen"]["label_title"] のようにアクセスします。

    質問者さんの「ツール」にそのようなコーディングをしたとすると、画面やその項目が増えたり減ったりしたときに、書き直し&再ビルド&再配布しなくて済むような手段はあるのでしょうか?

    もし手段がなければ、そのようなことを書くのは不適切ではないでしょうか?

    キャンセル

  • 2019/06/14 13:40

    文字列指定出来るんだからstring変数で指定できるわけで、なんで再ビルドって話になる?

    キャンセル

  • 2019/06/14 13:52

    使い方がイメージできていないのでしょう。

    編集ツールと、その他ツールがある。
    編集ツールは滅多に更新しないが、その他ツールは更新があり得る。
    その他ツールを更新した時、編集ツールを更新なしに使用したい。

    キャンセル

  • 2019/06/16 12:42

    jsonの中身を取ってきてからどうこうするのではなく、
    取ってくる段階でDictionary<String, Dictionary<String, String>>の形式に
    納めてしまう感じですか…なるほど…試してみます。

    キャンセル

0

このJSONを使用するアプリ側でも読み込みロジックが実装されていないのですか?
読み込みロジックはそこから持ってくればいいと思います。
そのアプリで読み込んだデータを格納するための構造体なりクラスなりがあると思うのですが、それをシリアライズ(デシリアライズ)すればいいのではないですか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/16 11:53

    アプリ側は他社さんの開発になっていて、未だサンプル画面程度しかできていません。
    サンプル画面ゆえに直値でやってそうですから、おそらくまだ読み込みロジックは無いのではないか…と思います。

    キャンセル

0

質問に対する 2019/06/16 12:57 の私のコメントで、「ピンと来てないようですので、後でそのサンプルを回答欄に書いておきます」と書きましたが、それを以下に書いておきます。

サードパーティ製のライブラリ MiniJSON と .NET 標準のライブラリの JavaScriptSerializer クラスを使ったサンプルです。

まず JSON 文字列ですが、質問のサンプルを使います。以下の通りです。質問に書かれていた「←(増える可能性あり)」は削除しましたが、構文に従って正しく追加・削除すればサンプルコードはそのままで対応できます。

{
  "LoginScreen":{
    "label_title":"Login Screen",
    "label_userId":"User ID",
    "label_password":"Password",
    "button_login":"LOGIN"
  },
  "UserLIstScreen":{
    "label_title":"User List",
    "label_userId":"User ID",
    "label_userName":"User Name",
    "label_userTell":"TEL",
    "label_phone":"Phone",
    "label_save":"Save"
  },
  "SettingListScreen" :{
    "label_title":"Setting",
    "label_storeName":"Store Name",
    "label_language":"Language",
    "button_save":"Save"
  }
}

サンプルコードは以下の通りです。上の JSON 文字列をテキストファイル TextFile2.txt に格納し、それを読んできて C# のオブジェクトにデシリアライズし、その後 JSON 文字列にシリアライズしています(シリアライズできないと編集結果を反映した JSON 文字列のファイルが作成できないので意味がないはず)。コメントにありますが、コードの前半が MiniJSON 利用、後半が JavaScriptSerializer 利用です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MiniJSON;
using System.IO;
using System.Web.Script.Serialization;

namespace ConsoleAppMiniJson
{
    class Program
    {
        static void Main(string[] args)
        {
            string path2 = @"C:\Users\...\TextFile2.txt";
            string jsonText2 = "";

            using (StreamReader sr = File.OpenText(path2))
            {
                jsonText2 = sr.ReadToEnd();
            }

            // MiniJSON 利用
            Console.WriteLine("--- MiniJSON 利用 -------------------------");
            var json2 = (Dictionary<string, object>)Json.Deserialize(jsonText2);
            foreach (KeyValuePair<string, object> kvp in json2)
            {
                Console.WriteLine("Key = {0}", kvp.Key);
                Dictionary<string, object> value = (Dictionary<string, object>)kvp.Value;
                foreach (KeyValuePair<string, object> kvp2 in value)
                {
                    Console.WriteLine("    Key = {0}, Value = {1}", kvp2.Key, (string)kvp2.Value);
                }
            }

            Console.WriteLine("----------------------");
            var json2String = Json.Serialize(json2);
            Console.WriteLine(json2String);


            // JavaScriptSerializer 利用
            Console.WriteLine("--- JavaScriptSerializer 利用 -------------");
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            var json4 = serializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(jsonText2);

            foreach (KeyValuePair<string, Dictionary<string, string>> kvp in json4)
            {
                Console.WriteLine("Key = {0}", kvp.Key);
                Dictionary<string, string> value = kvp.Value;
                foreach (KeyValuePair<string, string> kvp2 in value)
                {
                    Console.WriteLine("    Key = {0}, Value = {1}", kvp2.Key, kvp2.Value);
                }
            }

            Console.WriteLine("----------------------");
            var json4String = serializer.Serialize(json4);
            Console.WriteLine(json4String);
        }
    }
}

上のコードを、Windows 10 Pro 64-bit 上の VS2008 Pro で、.NET 3.5, プラットフォームターゲット AnyCPU に設定してコンパイル/実行し、以下の結果が得られます。

イメージ説明

見ての通り、デシリアライズした C# の Dictionary 型のオブジェクトから foreach ループを回して全ての key と value が取得できています。

取得した key と value を、質問者さんが作るという「ツール」のエディタに取り込んで編集し、編集結果から同様な C# の Dictionary 型のオブジェクトを作り、シリアライズするということを検討するということになると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • C#

    9215questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • JSON

    1484questions

    JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。