Json3 - lucyberryhub/WPF.Tutorial GitHub Wiki

πŸ’ Handling Data with Cherry Berry Flair! πŸŒΈπŸ“

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! πŸ’πŸŒˆ


πŸ’ Step 1: Let's Make Our Adorable Student Data Model πŸ’

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! πŸ’βœ¨

Example JSON Data Before Update

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 }
]

πŸ“ Step 2: Let's Create the SweetStorageHandler Class for Cherry Berry Magic πŸ“

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. πŸ’

πŸ“ Step 3: Let's Load Our Sweet Student Data with a Filter! πŸ“

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! πŸ“πŸ“š

πŸ’ Step 4: Sweetly Update Those Scores, Cherry Style! πŸ’

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. πŸ’βœ¨

πŸŽ€ Final Sweet Summary πŸŽ€

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!


πŸ’ Before the Update (Sweet JSON Data) πŸ’

Here’s how the student data looked before the update! πŸ“βœ¨

[
    { "FullName": "Alice Johnson", "Course": "Mathematics", "FinalExamScore": 80, "MidtermScore": 75, "QuizScore": 70 }
]

πŸ’ After the Update (Sweet JSON Data) πŸ’

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 }
]

🎯 Smart JSON Updates: How to Modify Specific Properties Without Changing Everything! πŸš€

πŸ“– Story: Meet Alex, the JSON Data Manager!

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! πŸ”₯


πŸ“ The Goal

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


πŸš€ Step 1: Loading JSON Data Like a Pro!

Before making any updates, we need to load the JSON file.
Alex wrote a smart function to do this:

πŸ“Œ LoadTableDataAsync() – Read JSON from File

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>();
    }
}

πŸ” What’s Happening Here?

βœ… 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).


πŸš€ Step 2: Updating Only Specific Properties!

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.

πŸ“Œ UpdateSelectedPropertiesAsync() – Modify Only What’s Needed!

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);
    }
}

πŸ” What’s Happening Here?

βœ… 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.


πŸš€ Step 3: Saving the Updated Data!

Once Alex successfully updated the properties, he needed to write the modified data back to the JSON file.

πŸ“Œ SaveTableDataAsync() – Save JSON Data Like a Pro!

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);
}

πŸ” What’s Happening Here?

βœ… 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.


πŸš€ Final Result: What the JSON Looks Like Before and After!

πŸ“Œ Original JSON File (Before Update)

[
    {
        "Id": "A123",
        "Name": "Wooden Chair",
        "Price": 50.00,
        "Discount": 5,
        "IsUpdated": false
    }
]

πŸ“Œ JSON File (After Update)

[
    {
        "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.


🎯 Why Is This Important?

πŸ’‘ Problem Before: Overwriting entire objects would remove unchanged data.
βœ… Now: We update only what’s needed, keeping everything else intact!


πŸš€ Bonus: Full Code for Quick Copy-Paste!

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);
}

πŸŽ‰ Conclusion: Alex Wins the Challenge! πŸ†

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.


⚠️ **GitHub.com Fallback** ⚠️