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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

JSON

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

2504閲覧

C#で読み込んだJsonファイルを編集したい

tranokado

総合スコア4

C#

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

JSON

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2022/04/16 09:45

前提・実現したいこと

WPFのアプリケーションで、読み込んだJsonファイルを編集しその変更を元のJsonファイルにも適用させたい

お世話になります。

現在WPFのアプリケーションを作成しており、

画面上の「開く」ボタンを押すことでエクスプローラーからjsonファイルを呼び出し
デシリアライズを行って画面上に必要なデータを表示する

部分までの機能を実装しています。

今後このデータを画面上で編集する機能を作成したいと思っているのですが、どのように実現するのがよいかわからず詰まってしまっている状況です。

jsonデータを編集する機能についてご教授いただけますと幸いです。

コードに基づく具体的な実現したいこと

  • subname欄に表示される項目押下時、その項目をsubnameから排除(取り除く)する機能
  • subname欄に表示される項目押下時、その項目を別のnameに移動またはコピーさせる機能
  • subnameを追加する機能

該当のソースコード

xaml

1<Window x:Class="ManagerEditor.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:ManagerEditor" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="800" Width="1100"> 9 <Grid> 10 <Grid x:Name="LeftRoot" Margin="10,10,-10,-10"> 11 <Grid.ColumnDefinitions> 12 <ColumnDefinition Width="10*"/> 13 <ColumnDefinition Width="27*"/> 14 </Grid.ColumnDefinitions> 15 <Button x:Name="OpenButton" Width="120" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" Content="開く" FontSize="18" Click="OpenButton_Click" /> 16 <TextBlock HorizontalAlignment="Left" Margin="10,60,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="31" Width="101" FontSize="18"><Run Language="ja-jp" Text="name"/><LineBreak/><Run Language="ja-jp"/></TextBlock> 17 <ListBox x:Name="UnitnameListBox" SelectionChanged="UnitNameListBox_SelectionChanged" Margin="0,100,0,12" ScrollViewer.HorizontalScrollBarVisibility="Disabled" DisplayMemberPath="name" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" HorizontalAlignment="Center" Width="275" /> 18 19 <TextBlock x:Name="Display" Grid.Column="2" HorizontalAlignment="Left" Margin="10,41,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="18"><Run Language="ja-jp" Text="name"/><LineBreak/><Run Language="ja-jp"/></TextBlock> 20 <TextBlock x:Name="DisplayBox" Grid.Column="1" HorizontalAlignment="Left" Margin="10,78,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="326" Height="46"/> 21 22 <TextBlock Grid.Column="1" HorizontalAlignment="Left" Margin="10,122,0,0" TextWrapping="Wrap" Text="URL" VerticalAlignment="Top" FontSize="18"/> 23 <TextBlock x:Name="URLBox" Grid.Column="1" HorizontalAlignment="Left" Margin="10,151,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="39" Width="595"/> 24 25 <TextBlock HorizontalAlignment="Left" Margin="10,194,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="31" Width="116" FontSize="18" Grid.Column="1"><Run Language="ja-jp" Text="subname"/></TextBlock> 26 <ListBox x:Name="SubnameListBox" Margin="10,230,27,12" ScrollViewer.HorizontalScrollBarVisibility="Disabled" DisplayMemberPath="subname" ItemsSource="{Binding / manager_list}" Grid.Column="1" /> 27 28 </Grid> 29 30 </Grid> 31</Window> 32

C#

1// Copyright © 2022 Tsubasa co., Ltd. All rights reserved. 2using Microsoft.Win32; 3using Newtonsoft.Json; 4using System; 5using System.Collections.Generic; 6using System.Diagnostics; 7using System.IO; 8using System.Windows; 9using System.Windows.Controls; 10 11namespace ManagerEditor 12{ 13 /// <summary> 14 /// メイン画面 15 /// </summary> 16 public partial class MainWindow : Window 17 { 18 public MainWindow() 19 { 20 InitializeComponent(); 21 } 22 23 private void Window_Loaded(object sender, RoutedEventArgs e) 24 { 25 26 } 27 28 PersonalData[] _data; 29 30 31 //Jsonデータのデシリアライズ、格納 32 private void OpenButton_Click(object sender, RoutedEventArgs e) 33 { 34 var dialog = new OpenFileDialog(); 35 dialog.Filter = "json|*.json"; 36 if (dialog.ShowDialog().Value) 37 { 38 var file = dialog.FileName; 39 40 try 41 { 42 var reader = new StreamReader(file); 43 var data = reader.ReadToEnd(); 44 reader.Close(); 45 _data = JsonConvert.DeserializeObject<PersonalData[]>(data); 46 47 } 48 catch (Exception error) 49 { 50 if (error.Message != null) 51 Debug.WriteLine(error.Message); 52 } 53 54 if (_data != null) 55 { 56 DataContext = _data; 57 } 58 59 URLBox.Text = ""; 60 DisplayBox.Text = ""; 61 } 62 } 63 64 //グループ押下時の挙動設定 65 private void UnitNameListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 66 { 67 if (UnitnameListBox.SelectedItem == null) return; 68 69 PersonalData item = (PersonalData)UnitnameListBox.SelectedItem; 70 DisplayBox.Text = item.name; 71 URLBox.Text = item.url; 72 } 73 74 } 75 public class Manager 76 { 77 public string subname { get; set; } 78 public string address { get; set; } 79 } 80 81 public class PersonalData 82 { 83 public string name { get; set; } 84 public string url { get; set; } 85 public List<Manager> manager_list { get; set; } 86 } 87 88} 89

json

1[ 2 { 3 "name": "tora", 4 "url": "https:tora.com", 5 "manager_list": [ 6 { 7 "subname": "tiger", 8 "address": "chiba" 9 }, 10 { 11 "subname": "tiger2", 12 "address": "tokyo" 13 } 14 15 ] 16 }, 17 { 18 "name": "cat", 19 "url": "https:cat.com", 20 "manager_list": [ 21 { 22 "subname": "neko", 23 "address": "kanagawa" 24 }, 25 { 26 "subname": "neko2", 27 "address": "yokohama" 28 } 29 ] 30 } 31] 32
TN8001👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/04/16 11:50

テキストファイルを編集する場合、一旦全部メモリに読み込んで編集し、編集したメモリの内容を丸ごとファイルに上書きするのが普通ですが(メモ帳でやるように)、JSON ファイルもテキストファイルなので同じことになる・・・なんてことは言われなくても分かってます? その方向で考えて実装してはいかがでしょう?
tranokado

2022/04/16 12:40

アドバイスありがとうございます! その方向で実装してみます。他の方の実装方針やアドバイスもお聞きしたいのでこの質問版はこのままにしておきます。自分の方でも進捗ありましたら共有させていただけたらと思います!
退会済みユーザー

退会済みユーザー

2022/04/16 13:21

メモ帳でやることを提案しているわけではなくて、もっとスマートにできる方法を検討してアプリを作ることを考えてはいかがと提案したつもりなのですが。(例えば、C# のオブジェクトにデシリアライズし、それを WPF の UI に表示してユーザーに編集してもらい、結果を JSON にシリアライズしてファイルに上書きするとか。インデントされた短い JSON 文字列ならメモ帳の方が簡単かもしれませんが)
tranokado

2022/04/16 13:26

> メモ帳でやることを提案しているわけではなくて すみません自分の伝え方も悪かったですね。 メモ帳でやるようなことをWPFに落とし込んで実装しようと考えていました。 >C# のオブジェクトにデシリアライズし、それを WPF の UI に表示してユーザーに編集してもらい、結果を JSON にシリアライズしてファイルに上書きする まさにこの方針ですね。この方針で実装に励んでみます。
guest

回答1

0

ベストアンサー

まず読み込み(DeserializeObject)と保存(SerializeObject)は、最初からセットで作っておきましょう(どうせ後で必要になる)

今後このデータを画面上で編集する機能を作成したいと思っているのですが、どのように実現するのがよいかわからず詰まってしまっている状況です。

「開く」ボタンはありますが、それ以外にButtonTextBoxが見当たりません。
そもそもどういうUIにするかも決まっていない(思いついていない?)ということでしょうか?

subname欄に表示される項目押下時、その項目をsubnameから排除(取り除く)する機能
subnameを追加する機能

この2つはRemoveAddするだけですが、ただのListではViewに反映させません。

WPFでアイテムの増減がある場合は、ObservableCollectionを使用します。
Listの代わりにObservableCollectionにするだけで、アイテムの増減をViewに通知して即時に反映されます。
ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs

移動やコピーは上2つの応用になりますが、問題はどういうUIにするかですね。
理想はsubnamenameに、ドラッグ&ドロップできることでしょうか。

ドラッグ&ドロップはまともにやるとかなり大変ですのでライブラリを使用しました。
使い方を調べる手間はかかりますが、実装は非常に楽になります。
NuGet Gallery | gong-wpf-dragdrop 3.1.1

punker76/gong-wpf-dragdrop: The GongSolutions.WPF.DragDrop library is a drag'n'drop framework for WPF

文字数ギリギリなので説明が少なめですが、不明な点があればコメントしてください。

xml

1<Window 2 x:Class="Qogxgft0o3rrwcj.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:dd="urn:gong-wpf-dragdrop" 6 xmlns:local="clr-namespace:Qogxgft0o3rrwcj" 7 Width="800" 8 Height="450"> 9 <Window.Resources> 10 <local:ManagerDropHandler x:Key="ManagerDropHandler" /> 11 <Style TargetType="Button"> 12 <Setter Property="Margin" Value="10,5" /> 13 <Setter Property="MinWidth" Value="80" /> 14 </Style> 15 <Style TargetType="GroupBox"> 16 <Setter Property="BorderThickness" Value="0" /> 17 </Style> 18 </Window.Resources> 19 20 <Grid Margin="10"> 21 <Grid.ColumnDefinitions> 22 <ColumnDefinition Width="10*" /> 23 <ColumnDefinition Width="27*" /> 24 </Grid.ColumnDefinitions> 25 26 <DockPanel> 27 <Button 28 Click="OpenButton_Click" 29 Content="開く" 30 DockPanel.Dock="Top" /> 31 <Button 32 Click="SaveAsButton_Click" 33 Content="保存" 34 DockPanel.Dock="Top" /> 35 36 <GroupBox Header="name"> 37 <ListBox 38 x:Name="nameListBox" 39 dd:DragDrop.DropHandler="{StaticResource ManagerDropHandler}" 40 dd:DragDrop.IsDragSource="True" 41 dd:DragDrop.IsDropTarget="True" 42 DisplayMemberPath="name" 43 IsSynchronizedWithCurrentItem="True" 44 ItemsSource="{Binding}" /> 45 </GroupBox> 46 </DockPanel> 47 48 <Grid Grid.Column="1"> 49 <Grid.RowDefinitions> 50 <RowDefinition Height="Auto" /> 51 <RowDefinition /> 52 <RowDefinition Height="Auto" /> 53 </Grid.RowDefinitions> 54 55 <DockPanel> 56 <StackPanel DockPanel.Dock="Right"> 57 <Button Click="Name_NewButton_Click" Content="新規" /> 58 <Button Click="Name_DelButton_Click" Content="削除" /> 59 </StackPanel> 60 <StackPanel> 61 <GroupBox DockPanel.Dock="Top" Header="name"> 62 <TextBox Text="{Binding /name, UpdateSourceTrigger=PropertyChanged}" /> 63 </GroupBox> 64 <GroupBox DockPanel.Dock="Top" Header="URL"> 65 <TextBox Text="{Binding /url, UpdateSourceTrigger=PropertyChanged}" /> 66 </GroupBox> 67 </StackPanel> 68 </DockPanel> 69 70 <GroupBox Grid.Row="1" Header="subname"> 71 <ListBox 72 x:Name="subNameListBox" 73 dd:DragDrop.IsDragSource="True" 74 dd:DragDrop.IsDropTarget="True" 75 DisplayMemberPath="subname" 76 IsSynchronizedWithCurrentItem="True" 77 ItemsSource="{Binding /manager_list}" /> 78 </GroupBox> 79 80 <DockPanel Grid.Row="2"> 81 <StackPanel DockPanel.Dock="Right"> 82 <Button Click="SubName_NewButton_Click" Content="新規" /> 83 <Button Click="SubName_DelButton_Click" Content="削除" /> 84 </StackPanel> 85 86 <StackPanel> 87 <GroupBox Header="subname"> 88 <TextBox Text="{Binding /manager_list/subname, UpdateSourceTrigger=PropertyChanged}" /> 89 </GroupBox> 90 <GroupBox Header="address"> 91 <TextBox Text="{Binding /manager_list/address, UpdateSourceTrigger=PropertyChanged}" /> 92 </GroupBox> 93 </StackPanel> 94 </DockPanel> 95 </Grid> 96 </Grid> 97</Window>

cs

1using System.Collections.ObjectModel; 2using System.IO; 3using System.Windows; 4using GongSolutions.Wpf.DragDrop; 5using GongSolutions.Wpf.DragDrop.Utilities; 6using Microsoft.Win32; 7using Newtonsoft.Json; 8 9namespace Qogxgft0o3rrwcj 10{ 11 public class Manager 12 { 13 public string subname { get; set; } 14 public string address { get; set; } 15 } 16 17 public class PersonalData 18 { 19 public string name { get; set; } 20 public string url { get; set; } 21 public ObservableCollection<Manager> manager_list { get; set; } 22 } 23 24 public partial class MainWindow : Window 25 { 26 private ObservableCollection<PersonalData> _data = new ObservableCollection<PersonalData>(); 27 28 public MainWindow() 29 { 30 InitializeComponent(); 31 DataContext = _data; 32 33 Dummy(); 34 } 35 36 private void OpenButton_Click(object sender, RoutedEventArgs e) 37 { 38 var dialog = new OpenFileDialog { Filter = "json|*.json", }; 39 if (dialog.ShowDialog() == true) 40 { 41 var json = File.ReadAllText(dialog.FileName); 42 _data = JsonConvert.DeserializeObject<ObservableCollection<PersonalData>>(json); 43 DataContext = _data; 44 } 45 } 46 private void SaveAsButton_Click(object sender, RoutedEventArgs e) 47 { 48 var dialog = new SaveFileDialog { Filter = "json|*.json", }; 49 if (dialog.ShowDialog() == true) 50 { 51 var json = JsonConvert.SerializeObject(_data, Formatting.Indented); 52 File.WriteAllText(dialog.FileName, json); 53 } 54 } 55 56 private void Name_NewButton_Click(object sender, RoutedEventArgs e) 57 { 58 var data = new PersonalData 59 { 60 name = "new name", 61 manager_list = new ObservableCollection<Manager>(), 62 }; 63 _data.Add(data); 64 nameListBox.SelectedItem = data; 65 } 66 private void Name_DelButton_Click(object sender, RoutedEventArgs e) 67 { 68 if (nameListBox.SelectedItem is PersonalData data) 69 { 70 _data.Remove(data); 71 } 72 } 73 74 private void SubName_NewButton_Click(object sender, RoutedEventArgs e) 75 { 76 if (nameListBox.SelectedItem is PersonalData data) 77 { 78 var manager = new Manager { subname = "new subname", }; 79 data.manager_list.Add(manager); 80 subNameListBox.SelectedItem = manager; 81 } 82 } 83 private void SubName_DelButton_Click(object sender, RoutedEventArgs e) 84 { 85 if (nameListBox.SelectedItem is PersonalData data 86 && subNameListBox.SelectedItem is Manager manager) 87 { 88 data.manager_list.Remove(manager); 89 } 90 } 91 92 93 private void Dummy() 94 { 95 if (File.Exists("test.json")) return; 96 97 var json = @" 98[ 99 { 100 ""name"": ""tora"", 101 ""url"": ""https:tora.com"", 102 ""manager_list"": [ 103 { 104 ""subname"": ""tiger"", 105 ""address"": ""chiba"" 106 }, 107 { 108 ""subname"": ""tiger2"", 109 ""address"": ""tokyo"" 110 } 111 ] 112 }, 113 { 114 ""name"": ""cat"", 115 ""url"": ""https:cat.com"", 116 ""manager_list"": [ 117 { 118 ""subname"": ""neko"", 119 ""address"": ""kanagawa"" 120 }, 121 { 122 ""subname"": ""neko2"", 123 ""address"": ""yokohama"" 124 } 125 ] 126 } 127] 128".Trim(); 129 130 File.WriteAllText("test.json", json); 131 } 132 } 133 134 135 class ManagerDropHandler : DefaultDropHandler 136 { 137 public override void DragOver(IDropInfo dropInfo) 138 { 139 // nameListBox内アイテム並び替え(デフォルト動作) 140 if (dropInfo.Data is PersonalData && dropInfo.TargetItem is PersonalData) 141 { 142 base.DragOver(dropInfo); 143 } 144 // subNameListBox→nameListBox アイテム移動・コピー(Ctrl+ドラッグ) 145 else if (dropInfo.Data is Manager && dropInfo.TargetItem is PersonalData) 146 { 147 var isCopy = dropInfo.KeyStates.HasFlag(DragDropKeyStates.ControlKey); 148 dropInfo.Effects = isCopy ? DragDropEffects.Copy : DragDropEffects.Move; 149 dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight; 150 } 151 } 152 153 public override void Drop(IDropInfo dropInfo) 154 { 155 if (dropInfo?.DragInfo == null) return; 156 157 // nameListBox内アイテム並び替え(デフォルト動作) 158 if (dropInfo.Data is PersonalData && dropInfo.TargetItem is PersonalData) 159 { 160 base.Drop(dropInfo); 161 } 162 // subNameListBox→nameListBox アイテム移動・コピー(Ctrl+ドラッグ) 163 else if (dropInfo.Data is Manager source && dropInfo.TargetItem is PersonalData target) 164 { 165 var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList(); 166 if (dropInfo.Effects == DragDropEffects.Copy) 167 { 168 var copy = new Manager { subname = source.subname, address = source.address, }; 169 target.manager_list.Add(copy); 170 } 171 else if (dropInfo.Effects == DragDropEffects.Move) 172 { 173 sourceList.Remove(source); 174 target.manager_list.Add(source); 175 } 176 } 177 } 178 } 179}

アプリ画像

当初はあまり「バインディング」せずに書いていたのですが、

  • 編集機能のUI(更新ボタン?)が悩ましい
  • ListBox.Items.Refresh()まみれになって訳が分からなくなった

ため諦めました^^;(わたしには難しすぎたw


やってみたこと・やろうとしたけどダメだったことを説明してください(やりたいことだけではただの作業依頼です)

xamlは大きく変わっているので「いろいろ試されてるのかな?」と推測できますが、C#コードは代わり映えしません。
コードにやってみたこと・やろうとしたけどダメだったことの痕跡がないので、第3者から見ると何もやっていないように見えてしまいます(痕跡が何もないよりは、エラーになっていても残っているほうがましです)

ヘルプ | 質問する時のヒント

投稿2022/04/17 04:55

編集2023/07/30 06:49
TN8001

総合スコア9326

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tranokado

2022/04/17 10:38

> TN8001さん 丁寧にお教えいただきありがとうございます。 自分の方でも作業依頼のように質問をしてしまい反省しております。 今後やってみたことなどを詳細に書いておこうと思います。 現在お教えいただいた方法も含めて自分の方で理解しながら、コード内に落とし込ませていただいておりますのでもうしばらくお待ちいただけたらと思います。
tranokado

2022/04/21 12:44

ご連絡遅くなり申し訳ありません。 いただいたコードを代替理解することができ、drag&dropの便利さを実感しています。 丁寧にご提案・ご指導くださりありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問