Json3 - lucyberryhub/WPF.Tutorial GitHub Wiki
Welcome to the Cherry Berry Tutorial of the Day! Today, weβre going to work on a super fun project thatβs all about handling student records, making updates, and saving them to a cherry-sweet JSON file! πβ¨
Weβll load data, update it, and save it back. All the method names and class names are going to be transformed into cherry-berry-tastic names. So, buckle up for the sweetest tutorial ever! ππ
This is where we define our Student data. It's like creating a cute profile card for each student! π»
public class SweetStudentRecord
{
public string FullName { get; set; } // Full name of our sweetie pie
public string Course { get; set; } // The course they are taking, like "Mathematics" or "Science" π‘
public int FinalExamScore { get; set; } // The score they got on the final exam π
public int MidtermScore { get; set; } // Their midterm score π
public int QuizScore { get; set; } // How they did on quizzes! π«
}
Explanation:
- SweetStudentRecord is our cute little data model for holding student scores for different subjects. Itβs all about those cherry-berry-perfect details! πβ¨
Here's what the student data might look like before we add those extra cherry-sweet updates! π
[
{ "FullName": "Alice Johnson", "Course": "Mathematics", "FinalExamScore": 80, "MidtermScore": 75, "QuizScore": 70 }
]
This class will be in charge of loading and saving all of our cute student data! Weβre making sure everything is organized and safe in the sweetest JSON file ever! π
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
public class SweetStorageHandler<T>
{
private const string jsonFilePath = "SweetStudentData.json"; // Oh so sweet file path! π
public async Task<List<T>> RetrieveSweetDataAsync(Func<T, bool> filter)
{
if (!File.Exists(jsonFilePath))
return new List<T>(); // No worries, just an empty list of sweeties π¬
string json = await File.ReadAllTextAsync(jsonFilePath); // Reading the sweet file π
var data = JsonSerializer.Deserialize<List<T>>(json) ?? new List<T>(); // Deserializing all our cute data!
return data.Where(filter).ToList(); // Filtering based on our cutie-pie condition π
}
public async Task SaveSweetUpdatedDataAsync(IEnumerable<T> newData, Func<T, bool> matchCondition)
{
try
{
List<T> existingSweeties = new List<T>();
if (File.Exists(jsonFilePath))
{
string existingJson = await File.ReadAllTextAsync(jsonFilePath); // Oh look, an old JSON file to update! πΈ
existingSweeties = JsonSerializer.Deserialize<List<T>>(existingJson) ?? new List<T>();
}
// Add or update data based on sweet condition π§Έ
foreach (var newEntry in newData)
{
var oldEntry = existingSweeties.FirstOrDefault(matchCondition);
if (oldEntry != null)
{
int index = existingSweeties.IndexOf(oldEntry);
if (index >= 0)
{
existingSweeties[index] = newEntry; // Update the record!
}
}
else
{
existingSweeties.Add(newEntry); // Adding a new sweetie π
}
}
string updatedJson = JsonSerializer.Serialize(existingSweeties, new JsonSerializerOptions { WriteIndented = true });
await File.WriteAllTextAsync(jsonFilePath, updatedJson); // Saving the updated data π
}
catch (Exception ex)
{
Console.WriteLine($"Oops, something went wrong while saving the sweet file: {ex.Message}");
}
}
}
Explanation:
- SweetStorageHandler handles loading and saving all our sweet student data. It's like our adorable helper that keeps everything safe and sound in the cherry-berry storage. π
We need to fetch records based on certain sweet conditions, like finding Mathematics students! How fun! π
public class SweetStudentManager
{
private readonly SweetStorageHandler<SweetStudentRecord> sweetStorageHandler = new SweetStorageHandler<SweetStudentRecord>();
public async Task<IEnumerable<SweetStudentRecord>> LoadSweetStudentRecordsAsync(Func<SweetStudentRecord, bool> filter)
{
try
{
var sweetStudents = await sweetStorageHandler.RetrieveSweetDataAsync(filter);
return sweetStudents;
}
catch (Exception ex)
{
Console.WriteLine($"Oops, couldnβt find your sweet students: {ex.Message}");
return Enumerable.Empty<SweetStudentRecord>(); // Just in case we donβt find anyone π
}
}
}
Explanation:
- LoadSweetStudentRecordsAsync is all about loading student records based on a cute filter. For example, we could filter students who are in Mathematics! ππ
When a teacher makes a change, we need to update the student's record in the cutest way possible! πβ¨
public class SweetScoreUpdater
{
private readonly SweetStorageHandler<SweetStudentRecord> sweetStorageHandler = new SweetStorageHandler<SweetStudentRecord>();
public async Task UpdateSweetStudentScoreAsync(object selectedRow)
{
if (selectedRow is SweetStudentRecord sweetStudent) // Check if we have a sweet student selected
{
var matchingSweetRecords = await sweetStorageHandler.RetrieveSweetDataAsync(record => record.FullName == sweetStudent.FullName && record.Course == sweetStudent.Course);
var foundRecord = matchingSweetRecords.FirstOrDefault(); // Find our cherry-berry sweetie!
if (foundRecord is SweetStudentRecord sweetData)
{
sweetData.FinalExamScore = sweetStudent.FinalExamScore; // Update those scores! π
sweetData.MidtermScore = sweetStudent.MidtermScore; // Midterm update π
sweetData.QuizScore = sweetStudent.QuizScore; // Quiz score update π
// Save the updated sweet data! π
await sweetStorageHandler.SaveSweetUpdatedDataAsync(matchingSweetRecords, record => record.FullName == sweetStudent.FullName && record.Course == sweetStudent.Course);
}
}
}
}
Explanation:
- UpdateSweetStudentScoreAsync is super sweet because it lets us update a studentβs scores. When a teacher clicks on a student, their scores are updated and saved back. πβ¨
In this cherry-berry sweet tutorial, we:
π Loaded student data from a JSON file!
π Found specific students by their name and course!
π Updated their scores when a teacher made a change!
π Saved the updated data back to the JSON file!
Hereβs how the student data looked before the update! πβ¨
[
{ "FullName": "Alice Johnson", "Course": "Mathematics", "FinalExamScore": 80, "MidtermScore": 75, "QuizScore": 70 }
]
And after the teacher made changes, hereβs the updated data in our cherry-tastic JSON! πβ¨
[
{ "FullName": "Alice Johnson", "Course": "Mathematics", "FinalExamScore": 95, "MidtermScore": 90, "QuizScore": 85 }
]
Alex is a young software engineer who loves playing with data! π
One day, Alex was given a special mission:
πΌ "You need to update only a few properties in a JSON file without affecting the rest of the data!"
Alex thought:
"If I just save the entire object, it will overwrite everything... but I only want to change a few things! How do I do that?" π€
π‘ That's when Alex discovered the secret techniqueβpartial updates! π₯
We have a list of data stored in a JSON file, and we want to:
β
Find the matching item in JSON
β
Update only the required properties (without touching others)
β
Set isUpdated = true
for the modified item
Before making any updates, we need to load the JSON file.
Alex wrote a smart function to do this:
public async Task<List<LucyModel>> LoadTableDataAsync()
{
// π If the file doesn't exist, return an empty list
if (!File.Exists(tempJsonFile)) return new List<LucyModel>();
using (var reader = new StreamReader(tempJsonFile))
{
// π Read the entire JSON file
string jsonData = await reader.ReadToEndAsync();
// π Convert JSON text into a List of objects
return JsonConvert.DeserializeObject<List<LucyModel>>(jsonData) ?? new List<LucyModel>();
}
}
β
First, it checks if the JSON file exists (to avoid errors).
β
It reads the JSON content asynchronously.
β
It deserializes the JSON into a list of LucyModel
objects.
β
If the file is empty, it returns an empty list (so the app doesnβt crash).
Now, Alex wants to update only selected properties.
π‘ Example Scenario:
πΉ We have a list of furniture in a JSON file.
πΉ We only want to update the price and discount of a chair without modifying other properties like color, brand, etc.
public async Task UpdateSelectedPropertiesAsync(string itemId, decimal newPrice, int newDiscount)
{
// π Step 1: Load existing data from JSON
var items = await LoadTableDataAsync();
// π Step 2: Find the item that needs to be updated
var itemToUpdate = items.FirstOrDefault(item => item.Id == itemId);
if (itemToUpdate != null)
{
// β¨ Step 3: Update ONLY the required properties
itemToUpdate.Price = newPrice;
itemToUpdate.Discount = newDiscount;
itemToUpdate.IsUpdated = true; // β
Mark as updated
// πΎ Step 4: Save changes back to JSON
await SaveTableDataAsync(items);
}
}
β
Finds the correct item using item.Id == itemId
.
β
Updates only Price
and Discount
(does NOT overwrite other properties).
β
Marks isUpdated = true
so we know which item was changed.
β
Saves the updated list back to the JSON file.
Once Alex successfully updated the properties, he needed to write the modified data back to the JSON file.
public async Task SaveTableDataAsync(List<LucyModel> updatedData)
{
// π Convert the updated list back to JSON format
string updatedJson = JsonConvert.SerializeObject(updatedData, Formatting.Indented);
// πΎ Save the JSON to file
await File.WriteAllTextAsync(tempJsonFile, updatedJson);
}
β
Serializes the list of objects back to JSON format.
β
Overwrites the file with the new data (preserving the updates).
β
Uses Formatting.Indented
to make the JSON human-readable.
[
{
"Id": "A123",
"Name": "Wooden Chair",
"Price": 50.00,
"Discount": 5,
"IsUpdated": false
}
]
[
{
"Id": "A123",
"Name": "Wooden Chair",
"Price": 45.00,
"Discount": 10,
"IsUpdated": true
}
]
πΉ Only Price
and Discount
changed!
πΉ Other properties (like Name
) remained the same.
πΉ The isUpdated
flag helps track changes.
π‘ Problem Before: Overwriting entire objects would remove unchanged data.
β
Now: We update only whatβs needed, keeping everything else intact!
public async Task<List<LucyModel>> LoadTableDataAsync()
{
if (!File.Exists(tempJsonFile)) return new List<LucyModel>();
using (var reader = new StreamReader(tempJsonFile))
{
string jsonData = await reader.ReadToEndAsync();
return JsonConvert.DeserializeObject<List<LucyModel>>(jsonData) ?? new List<LucyModel>();
}
}
public async Task UpdateSelectedPropertiesAsync(string itemId, decimal newPrice, int newDiscount)
{
var items = await LoadTableDataAsync();
var itemToUpdate = items.FirstOrDefault(item => item.Id == itemId);
if (itemToUpdate != null)
{
itemToUpdate.Price = newPrice;
itemToUpdate.Discount = newDiscount;
itemToUpdate.IsUpdated = true;
await SaveTableDataAsync(items);
}
}
public async Task SaveTableDataAsync(List<LucyModel> updatedData)
{
string updatedJson = JsonConvert.SerializeObject(updatedData, Formatting.Indented);
await File.WriteAllTextAsync(tempJsonFile, updatedJson);
}
With this smart JSON update method, Alex was able to:
β
Modify only selected properties instead of overwriting the entire object.
β
Keep other properties safe from accidental changes.
β
Track which items were modified using isUpdated = true
.