1) Wprowadzenie – po co nam pliki TXT i JSON
Kiedy aplikacja ma coś zapamiętać na później (np. listę wyników, ustawienia, notatki), potrzebuje trwałego magazynu. Najprościej: pliki. Dwa najczęstsze formaty w praktyce to TXT (zwykły tekst) i JSON (tekst, ale o strukturze obiektów). TXT jest jak prosty notatnik – czytelny dla człowieka, szybki do ogarnięcia. JSON to notatnik + logiczne szuflady: pola, listy, obiekty.
W aplikacjach desktopowych (WinForms/WPF) czy mobilnych (.NET MAUI) zasada jest ta sama: zapis/odczyt to System.IO.*, a JSON to System.Text.Json. Różnice? Głównie lokalizacja plików (ścieżki) i asynchroniczność (na telefonie blokowanie wątku UI to grzech).
Dobrze od razu zapamiętać trzy filary:
- Ścieżka – gdzie zapisać (katalog dostępny dla aplikacji).
- Kodowanie – używaj UTF-8, żeby „ąęłń” nie zamieniło się w krzaczki.
- Obsługa błędów – pliku może nie być; odczyt może się nie udać.
2) TXT – zwykły tekst: kiedy wystarczy i jak to ugryźć
TXT sprawdza się, gdy dane są proste: linie tekstu, lista rekordów „pola rozdzielone przecinkiem”, logi. Plusy: minimalny narzut, zero dodatkowych bibliotek, łatwo otworzyć w Notatniku. Minus: musisz sam umówić się na format, np. „imię, wynik”.
Najprostszy zapis/odczyt jednego łańcucha:
using System.Text; // dla UTF-8
string text = "Jan Kowalski,10\nPiotr Nowak,2\nJan Iksiński,33";
File.WriteAllText("dane.txt", text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); // UTF-8 bez BOM
string odczytane = File.ReadAllText("dane.txt", Encoding.UTF8);
Console.WriteLine(odczytane);
Gdy dane są „rekordami po liniach”, czytamy linia po linii i „rozcinamy”:
string[] lines = File.ReadAllLines("dane.txt", Encoding.UTF8);
foreach (string line in lines)
{
if (string.IsNullOrWhiteSpace(line)) continue;
string[] pola = line.Split(',', StringSplitOptions.TrimEntries);
if (pola.Length >= 2)
Console.WriteLine($"Imię: {pola[0]}, Wynik: {pola[1]}");
}
Pułapki TXT:
• Ustal separator (przecinek, średnik, tab) i trzymaj się go.
• Używaj Encoding.UTF8, żeby polskie znaki były OK.
• Waliduj dane: pusta linia, brak przecinka, błędny wynik – to się zdarza.
3) JSON – zapis obiektów i list obiektów jak człowiek
JSON to tekst, ale z kluczami i wartościami, tablicami i zagnieżdżeniami. Idealny do konfiguracji, API, list rekordów. W .NET używamy System.Text.Json (szybki, wbudowany). Zasada: „obiekt ↔ JSON” to serializacja, a w drugą stronę deserializacja.
Klasa + zapis jednego obiektu:
using System.Text.Json;
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
// SERIALIZACJA (zapis)
var weather = new WeatherForecast
{
Date = DateTimeOffset.Now,
TemperatureCelsius = 18,
Summary = "Chłodno"
};
var options = new JsonSerializerOptions { WriteIndented = true }; // czytelniej
string json = JsonSerializer.Serialize(weather, options);
File.WriteAllText("pogoda.json", json, new UTF8Encoding(false));
Odczyt (deserializacja):
string odczytanyJson = File.ReadAllText("pogoda.json", Encoding.UTF8);
WeatherForecast? pogoda = JsonSerializer.Deserialize<WeatherForecast>(odczytanyJson);
Console.WriteLine($"{pogoda?.Date:yyyy-MM-dd HH:mm} | {pogoda?.TemperatureCelsius}°C | {pogoda?.Summary}");
Najczęstszy case w projektach: lista obiektów (np. wyniki uczniów):
public record Score(string Name, int Points);
var scores = new List<Score>
{
new("Jan", 10),
new("Piotr", 2),
new("Ala", 33),
};
string jsonList = JsonSerializer.Serialize(scores, new JsonSerializerOptions{ WriteIndented = true });
File.WriteAllText("scores.json", jsonList, new UTF8Encoding(false));
// ...odczyt później:
string read = File.ReadAllText("scores.json", Encoding.UTF8);
List<Score>? loaded = JsonSerializer.Deserialize<List<Score>>(read) ?? new();
Console.WriteLine($"Wczytano {loaded.Count} rekordów");
Tipy JSON:
• WriteIndented = true – plik czytelny dla człowieka.
• Nazwy pól camelCase? options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
• Własne nazwy pól: [JsonPropertyName("tempC")] nad właściwością.
• Daty: trzymaj jeden format (tu DateTimeOffset bezpieczniejszy niż DateTime).
4) Wersje asynchroniczne – szczególnie ważne w .NET MAUI / UI
Plik bywa „wolny”. W aplikacjach z interfejsem nie blokuj wątku UI. Użyj ReadAllTextAsync/WriteAllTextAsync albo strumieni (FileStream) z await.
// ZAPIS ASYNC
await File.WriteAllTextAsync("dane.txt", "Hello async!", new UTF8Encoding(false));
// ODCZYT ASYNC
string txt = await File.ReadAllTextAsync("dane.txt", Encoding.UTF8);
Console.WriteLine(txt);
Ze strumieniem (większe pliki, kontrola buforów):
await using var fs = new FileStream("pogoda.json", FileMode.Create, FileAccess.Write, FileShare.None);
await JsonSerializer.SerializeAsync(fs, weather, new JsonSerializerOptions{ WriteIndented = true });
await using var fs2 = new FileStream("pogoda.json", FileMode.Open, FileAccess.Read, FileShare.Read);
var loadedWeather = await JsonSerializer.DeserializeAsync<WeatherForecast>(fs2);
5) Gdzie zapisywać pliki – ścieżki w desktopie i MAUI
Najczęstszy błąd początkujących: zapisywanie „obok EXE” albo w C:\. Dziś systemy mają polityki uprawnień – zapisuj do katalogu użytkownika/aplikacji.
Windows (WPF/WinForms):
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
// np. C:\Users\Uzytkownik\AppData\Local
string path = Path.Combine(folder, "MojaAplikacja", "scores.json");
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
File.WriteAllText(path, json, new UTF8Encoding(false));
.NET MAUI (Android/iOS/Windows/macOS):
using Microsoft.Maui.Storage;
string appDir = FileSystem.AppDataDirectory; // katalog prywatny aplikacji
string path = Path.Combine(appDir, "settings.json");
await File.WriteAllTextAsync(path, json, new UTF8Encoding(false));
Uwagi:
• Zawsze Directory.CreateDirectory(...) przed zapisem.
• Do ścieżek używaj Path.Combine.
• Nie zakładaj, że plik istnieje – sprawdzaj.
6) Obsługa błędów i odporność na realny świat
Pliku może nie być. JSON może być uszkodzony. Użytkownik mógł zamknąć aplikację w połowie zapisu. Pisz defensywnie.
try
{
if (!File.Exists("scores.json"))
{
Console.WriteLine("Brak pliku – tworzę pustą listę.");
}
else
{
string data = File.ReadAllText("scores.json", Encoding.UTF8);
var scores = JsonSerializer.Deserialize<List<Score>>(data) ?? new();
Console.WriteLine($"OK, wczytano {scores.Count} rekordów.");
}
}
catch (JsonException ex)
{
Console.WriteLine("Błąd JSON – plik uszkodzony? " + ex.Message);
}
catch (IOException ex)
{
Console.WriteLine("Błąd dysku/ścieżki: " + ex.Message);
}
Dobre nawyki:
• Twórz kopię zapasową przed nadpisaniem: zapisz do *.tmp, potem File.Replace.
• Waliduj dane po wczytaniu (np. punktów nie ujemnych).
• Loguj błędy – nawet do prostego log.txt.
7) TXT czy JSON? – szybka mapa decyzji
Wybierz TXT, gdy:
• chcesz po prostu zrzucić log albo listę wartości;
• dane są „jednowymiarowe” (np. jedna wartość na linię);
• wymieniasz plik z użytkownikiem „ręcznie” (łatwo edytuje).
Wybierz JSON, gdy:
• masz obiekty z polami i listy obiektów;
• chcesz łatwo mapować z/do klas (serializacja);
• myślisz o API i kompatybilności z innymi aplikacjami.
8) Kilka przydatnych sztuczek (warto znać na lekcji i maturze)
Własne nazwy pól / ignorowanie pól:
public class Person
{
[JsonPropertyName("fullName")]
public string Name { get; set; } = "";
[JsonIgnore]
public string? SecretNote { get; set; }
}
Formatowanie dat (string, jeśli chcesz mieć kontrolę):
public class Event
{
public string Title { get; set; } = "";
public string DateIso { get; set; } = DateTimeOffset.Now.ToString("O"); // ISO 8601
}
Ładowanie „bez wybuchu”, gdy plik nie istnieje:
static T LoadOrDefault<T>(string path, T @default) where T : class
{
if (!File.Exists(path)) return @default;
try
{
string s = File.ReadAllText(path, Encoding.UTF8);
return JsonSerializer.Deserialize<T>(s) ?? @default;
}
catch { return @default; }
}
CSV jako „bogatszy TXT” (często wystarcza):
string csv = "name;points\nJan;10\nAla;33";
File.WriteAllText("wyniki.csv", csv, new UTF8Encoding(false));
9) Prosty „mini-projekt”: wyniki uczniów (TXT i JSON)
Ta sama funkcjonalność w dwóch wersjach – uczniowie zobaczą różnicę.
TXT – zapis i odczyt:
public record Score(string Name, int Points);
static void SaveTxt(string path, IEnumerable<Score> scores)
{
var lines = scores.Select(s => $"{s.Name},{s.Points}");
File.WriteAllLines(path, lines, new UTF8Encoding(false));
}
static List<Score> LoadTxt(string path)
{
var list = new List<Score>();
if (!File.Exists(path)) return list;
foreach (var line in File.ReadAllLines(path, Encoding.UTF8))
{
var p = line.Split(',', StringSplitOptions.TrimEntries);
if (p.Length == 2 && int.TryParse(p[1], out int pts))
list.Add(new Score(p[0], pts));
}
return list;
}
JSON – zapis i odczyt:
static async Task SaveJsonAsync(string path, List<Score> scores)
{
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
await using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
await JsonSerializer.SerializeAsync(fs, scores, new JsonSerializerOptions{ WriteIndented = true });
}
static async Task<List<Score>> LoadJsonAsync(string path)
{
if (!File.Exists(path)) return new();
await using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
return await JsonSerializer.DeserializeAsync<List<Score>>(fs) ?? new();
}
10) Aplikacje mobilne (MAUI) vs desktop (WPF) – co się różni w praktyce
W WPF zapiszesz najczęściej do LocalApplicationData (patrz wyżej). W MAUI użyj FileSystem.AppDataDirectory. Na Androidzie/iOS pliki trzymamy w prywatnym katalogu aplikacji – nie zakładaj, że użytkownik „zobaczy” je w systemowym menedżerze plików.
W MAUI zawsze używaj wersji asynchronicznych – przewijanie listy i jednoczesny zapis JSON-a? Da się, jeśli nie blokujesz wątku UI. Pamiętaj też, że w mobilkach ścieżki zmieniają się między platformami – FileSystem.AppDataDirectory wszystko załatwia.
Na desktopie bonus: możesz użyć OpenFileDialog/SaveFileDialog (WPF) i pozwolić użytkownikowi wskazać miejsce zapisu. Na mobilkach – zwykle nie.
11) Podsumowanie – co zabrać „na maturę”
• TXT – najprostszy zapis tekstu; sam pilnujesz formatu.
• JSON – naturalny zapis obiektów i list; łatwa serializacja.
• Zawsze dbaj o UTF-8 i obsługę błędów.
• W aplikacjach z UI – async.
• Zapisuj do katalogu użytkownika/aplikacji, nie do C:\.
• Potrafisz zapisać i odczytać listę obiektów? To realne punkty.
12) Mini-zadania (pod klasę / pod maturę)
- TXT → obiekt: Wczytaj
dane.txtz liniami „Imię,Punkty”, zmapuj doList<Score>, wypisz TOP-3. - JSON → edycja → JSON: Wczytaj
scores.json, dodaj nowy rekord i zapisz ładnie sformatowany (WriteIndented). - Asynchroniczność: Zaimplementuj
SaveJsonAsync/LoadJsonAsyncw MAUI (użyjFileSystem.AppDataDirectory). - Odporność: Zabezpiecz odczyt przed uszkodzonym JSON – w razie błędu zwróć pustą listę i zaloguj błąd.
- Własne nazwy pól: Zmień nazwy właściwości w JSON za pomocą
[JsonPropertyName]. - Daty: Zapisz listę zdarzeń z datą ISO 8601, wczytaj i posortuj po dacie malejąco.

