1. Mentalny model JSON i XML
Szczerze mowiac, programista rzadko budzi sie rano z mysla: „A moze by tak dzis porownac JSON i XML, zanim kawa wystygnie”. Zwykle wybor formatu dzieje sie dlatego, ze ktos inny juz podjal polowe decyzji za ciebie: zewnetrzny serwis zwraca XML, stare korporacyjne API wymaga SOAP, ksiegowosc przysyla „wspanialy” plik XML, a twoj projekt, przeciwnie, chce prosty raport w JSON. I wtedy stoisz miedzy dwoma swiatami: kompaktowym „dane jako struktura” (JSON) i bardziej „dokumentowym” swiatem tagow (XML).
Wazne jest zrozumienie: JSON i XML to nie „lepszy/gorszy”, tylko rozne modele reprezentacji danych. A jesli wybierzesz format bez kryteriow, latwo dostaniesz sytuacje, w ktorej dane niby sie przesylaja, ale kazdy kolejny krok rozwoju zaczyna przypominac quest „zgadnij, gdzie zgubilismy typy / kolejnosc / zagniezdzenie / escapowanie”.
Zeby wybierac format swiadomie, warto najpierw uzgodnic, jak w ogole mentalnie „widzimy” dane w kazdym z formatow. To jak wybor pojemnika na jedzenie: zupa w woreczku jest technicznie mozliwa, ale nie musisz tak cierpiec. JSON i XML inaczej odpowiadaja na pytanie „czym jest struktura danych” i to bezposrednio wplywa na to, jak latwo bedzie nam te dane czytac, walidowac i generowac w Kotlin.
JSON jako drzewo wartosci
JSON to drzewo, w ktorym wezly maja cztery podstawowe typy: obiekt (pary klucz-wartosc), tablica (uporzadkowana lista), prymityw (string/liczba/boolean) oraz null. W poprzednich wykladach doslownie pracowalismy z tym jak z drzewem JsonElement, i to odczucie jest bardzo trafne: kazdy wezel to wartosc, ktora ma typ, i typ mozna sprawdzic.
Efekt praktyczny: jesli widzisz w JSON true albo 123, to juz rozumiesz, ze to boolean i liczba (chociaz czasem trzeba rzutowac ze stringa, gdy dane sa „brudne”).
XML jako drzewo elementow dokumentu
XML tez jest drzewem, ale wezly to elementy (tagi) z nazwami, atrybutami i zawartoscia tekstowa. I tu zaczyna sie filozofia: XML historycznie wyrastal jako format dokumentow, gdzie liczy sie znacznikowanie, zagniezdzenie i znaczenie tagow, a nie „typ wartosci” jako taki.
Efekt praktyczny: prawie wszystko w XML z natury przypomina stringi (tekst w elemencie albo wartosc atrybutu). Typowanie dodajesz „umowa”: na przyklad <age>21</age> jest liczba tylko dlatego, ze tak ustaliles i twoj kod tak to sparsowal.
3. Dane vs dokument
W realnych projektach najbardziej pomocne pytanie brzmi tak: przekazujesz dane (strukture dla programu) czy dokument (tekst ze znacznikowaniem, gdzie wazne sa elementy, atrybuty, kolejnosc, czasem mieszana zawartosc)? To nie jest „czysta teoria” — to szybki sposob, zeby przestac sie klocic w stylu „JSON jest modny, XML jest stary”, i zaczac dobierac format do zadania.
JSON zwykle wygrywa, gdy opisujesz: „oto obiekt, ma pola, pole ma typ, czasem tablice”. To typowy format dla API, konfiguracji, raportow, wymiany danych miedzy serwisami.
XML czesto wygrywa, gdy opisujesz: „oto dokument, ma strukture, elementy, metadane w atrybutach, czasem wazna jest kolejnosc, czasem wazne sa przestrzenie nazw”. To typowy format dla obiegu dokumentow, niektorych integracji, starych standardow i w ogole swiata, w ktorym „tagi” sa wazniejsze od „typow”.
4. Porownanie wedlug kryteriow
Ponizej — praktyczna tabela kryteriow. To nie „prawda ostateczna”, ale dobry check-list, ktory naprawde pomaga podjac decyzje i potem jej nie zalowac.
| Kryterium | JSON | XML | Co to oznacza dla kodu w Kotlin |
|---|---|---|---|
| Czytelnosc dla „danych” | Zwykle prosciej i krocej | Zwykle bardziej wieloslowne | JSON szybciej sie czyta i latwiej generuje |
| „Typy” prymitywow | Typy sa widoczne (true, 123, "text", null) | Wszystko wyglada jak tekst | W JSON jest mniej „zgadywania typow”, w XML parsowanie prawie zawsze robi sie recznie/regulami |
| Tablice/listy | Naturalne [...] | Robione przez powtarzajace sie tagi | W JSON latwiej wyrazic „liste jednorodnych rzeczy” |
| Atrybuty jako osobna encja | Nie (wszystko to pola) | Sa atrybuty | XML wygodnie trzyma „metadane” obok elementu |
| Kolejnosc | W tablicy jest wazna, w obiekcie zwykle nie | Kolejnosc elementow czesto jest postrzegana jako wazna | Jesli kolejnosc jest istotna jako czesc znaczenia — XML moze byc bardziej naturalny |
| Komentarze | W scislym JSON ich nie ma | W XML sa komentarze | Dla „ludzkich” konfiguracji XML bywa wygodniejszy (ale to kij o dwoch koncach) |
| Schematy/walidacja | Sa podejscia, ale czesto „na umowe” | Historycznie silna ekosystem schematow | Jesli masz twarde standardy i kontrakty, swiat XML czesto bywa bardziej „uregulowany” |
| Rozmiar/szum | Zwykle mniejszy | Zwykle wiekszy | Dla logow/odpowiedzi sieciowych JSON jest korzystniejszy objetosciowo |
| Integracje | Bardzo powszechny w nowoczesnych API | Bardzo powszechny w starych/korporacyjnych standardach | Wybor czesto jest wymuszony przez srodowisko zewnetrzne |
Male spostrzezenie z zycia: narzedzia i ekosystem Kotlin lubia JSON, bo to wygodny format dla maszynowych raportow. Na przyklad dokumentacja migracji na K2 wspomina o zapisywaniu build reports w formacie JSON, czyli JSON realnie jest uzywany jako „raport maszynowo czytelny”.
5. Ten sam sens w JSON i XML
Porownywanie formatow bez przykladow to jak tlumaczenie plywania z ksiazki: technicznie mozliwe, ale lepiej nie. Wezmy ten sam sens i zapiszmy go na dwa sposoby. Niech mamy uzytkownika i jego tagi.
Przyklad danych
- uzytkownik: id=1, name="Alice"
- tagger: ["kotlin", "json"]
Wariant JSON
fun main() {
val json = """{"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}"""
println(json) // {"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}
}
Tutaj tablica jest tablica, liczby sa liczbami, stringi sa stringami. Bez niespodzianek: struktura jest bardzo bliska strukturom danych Kotlin (Map/List/prymitywy).
Wariant XML
fun main() {
val xml = """
<root>
<user>
<id>1</id>
<name>Alice</name>
</user>
<tags>
<tag>kotlin</tag>
<tag>json</tag>
</tags>
</root>
""".trimIndent()
println(xml)
}
XML wyglada bardziej „dokumentowo”: duzo tagow, jawna hierarchia, powtarzalne elementy dla listy. I zwroc uwage: liczba 1 jest tu tekstem "<id>1</id>". To, ze jest to Int, wie tylko twoj parser (albo twoja walidacja).
6. Typowanie i atrybuty
Typowanie i dlaczego w XML wszystko nagle jest stringiem
Jesli kiedys zlapales buga „dlaczego wiek 21 jest porownywany jak string i wychodzi, ze 9 jest „wieksze” niz 21”, to intuicyjnie rozumiesz, czemu typowanie jest wazne. JSON pomaga tym, ze prymitywy sa typowane na poziomie formatu. XML zostawia typowanie na sumieniu umowy i kodu, ktory czyta dokument.
Efekt praktyczny nr 1: walidacja jest potrzebna w kazdym formacie
I JSON, i XML moga byc „syntaktycznie poprawne”, ale „logicznie smieciowe”. Juz to omawialismy na JSON: parseToJsonElement() odpowiada tylko na pytanie „czy to jest poprawny JSON”, a nie na pytanie „czy pasuja nam wartosci”. Z XML jest podobnie: nawet jesli XML jest dobrze uformowany, moze zawierac puste pola, bledne zakresy, nieoczekiwane elementy.
Efekt praktyczny nr 2: w XML czesciej piszesz konwersje
W XML prawie zawsze robisz String -> Int? / String -> Boolean? samodzielnie. W JSON tez sie to zdarza (zwlaszcza gdy dane sa „brudne”), ale domyslnie typy sa przynajmniej blizej oczekiwanych.
Atrybuty XML: sila i zrodlo wiecznych sporow
Atrybuty to jeden z powodow, dla ktorych XML nadal zyje i nie planuje isc „na emeryture do muzeum starozytnych technologii”. Atrybuty pozwalaja przechowywac metadane bezposrednio na elemencie. To wygodne, ale tez wywoluje „religijne wojny”: co zrobic atrybutem, a co elementem?
Porownajmy dwa warianty tego samego sensu:
fun main() {
val xmlWithAttribute = """<user id="1"><name>Alice</name></user>"""
val xmlWithElement = """<user><id>1</id><name>Alice</name></user>"""
println(xmlWithAttribute) // <user id="1"><name>Alice</name></user>
println(xmlWithElement) // <user><id>1</id><name>Alice</name></user>
}
W JSON prawie nie ma takiego wyboru: po prostu zrobilbys "id": 1. I to ma zalete: mniej wariantow — mniej zamieszania w danych i w kodzie.
7. Gdzie JSON i XML maja sens w projektach Kotlin
Gdzie JSON jest szczegolnie dobry w naszym kursie
Budowalismy juz raporty, agregowalismy dane, robilismy pipeline’y „wzielismy kolekcje → policzylismy → zbudowalismy wynik”. I do takich zadan JSON jest prawie idealnym „kontenerem na rezultat”: latwo go zlozyc recznie, latwo zapisac, latwo wyslac jako strukture.
Swoja droga, w ekosystemie Kotlin serializacja to nie „egzotyka”, tylko stabilne narzedzie: tabela stabilnosci komponentow Kotlin osobno wspomina kotlinx-serialization jako stabilna biblioteke.
Mini-przyklad: raport JSON dla naszego trackera wydatkow
Wyobrazmy sobie, ze w naszej praktycznej aplikacji (umownie „Expense Tracker”) liczymy sume wydatkow i chcemy zrobic raport.
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
data class Expense(val title: String, val amount: Int)
fun buildReport(expenses: List<Expense>): JsonObject {
val total = expenses.sumOf { it.amount }
return buildJsonObject {
put("total", JsonPrimitive(total))
put("count", JsonPrimitive(expenses.size))
}
}
fun main() {
val expenses = listOf(
Expense("Coffee", 5),
Expense("Pizza", 12),
)
val report = buildReport(expenses)
println(report) // {"total":17,"count":2}
}
Zauwaz, jak naturalnie to pasuje do kolekcji Kotlin: policzylismy liczby i wlozylismy je do JSON jako liczby, nie gubiac typow „po drodze”.
Gdzie XML moze byc bardziej na miejscu
Czasem wybor „XML” nie wynika z checi przygod, tylko z tego, ze podlaczasz sie do swiata, w ktorym XML jest juz standardem. To moga byc dokumenty, formaty integracyjne, standardy branzowe, a czasem po prostu „tak sie historycznie ulozylo”.
W swiecie Kotlin/JVM XML czesto idzie obok bibliotek Java i standardowych API. Nawet w zmianach Kotlin mozna spotkac wzmianki o infrastrukturze zwiazanej z org.w3c (to rodzina interfejsow, historycznie kojarzona z modelem DOM dla XML).
Ale kluczowy moment jest taki: w XML czesciej trzeba z gory uzgodnic „zasady reprezentacji”. Na przyklad: jak kodujemy liste — powtarzajacymi sie tagami? osobnym kontenerem <items>? jak nazywamy elementy? To nie jest zle, to po prostu inny styl.
8. Walidacja i generowanie: jak nie zepsuc projektu
Format to nie reguly biznesowe
Teraz wazny moment, ktory czesto psuje projekty: ludzie mysla, ze jesli format jest wybrany „dobrze”, to walidacja nie jest juz potrzebna. Niestety format to tylko skladnia i bazowa struktura.
Robilismy juz warstwe Ok/Error dla walidacji JSON. I to dobra praktyka: niezaleznie od tego, czy masz JSON czy XML, trzymasz w glowie ta sama architekture:
flowchart TD
A["Tekst (JSON lub XML)"] --> B["Parsowanie: 'czy to w ogole poprawny format?'"]
B --> C["Walidacja: 'czy dane pasuja do regul aplikacji?'"]
C --> D["Model domenowy / obliczenia / raporty"]
Ten schemat ratuje przed typowym bolem: „czasem nam pada na produkcji”, bo ktos przyslal poprawny plik, ale z pustym id albo ujemna suma.
Szybki algorytm wyboru formatu
Czasem chce sie prostej zasady, zeby nie klocic sie bez konca. Niech to bedzie nie zasada, a krotki algorytm podejmowania decyzji, ktory w wiekszosci zadan edukacyjnych i praktycznych dziala calkiem dobrze.
Najpierw odpowiadasz na pytanie: struktura danych jest stabilna czy nie? Jesli struktura jest niestabilna, dla JSON masz swietne narzedzie — drzewo JsonElement i bezpieczne wydobywanie.
Potem pytasz: co jest wazniejsze — „dane jako obiekt/tablica” czy „dokument jako znacznikowanie”? Jesli wazniejsze sa dane, czesciej wybiera sie JSON. Jesli wazniejsza jest dokumentowosc, standardy i zgodnosc z istniejacymi kontraktami XML — XML.
I na koniec — najbardziej realne kryterium: kto jest twoim „sasiadem integracyjnym”? Jesli zewnetrzny swiat zwraca XML, nie pokonasz rzeczywistosci mowa motywacyjna o JSON — bedziesz czytac XML. Jesli zewnetrzny swiat mowi JSON — bedziesz pracowac z JSON.
Mini-niuans: nie sklejaj stringow
To mala rada z serii „na grabie depcza wszyscy, ale mozna przynajmniej w miekkich kapciach”. Jesli generujesz JSON, staraj sie skladac go jako strukture (buildJsonObject, buildJsonArray), a nie sklejac stringami.
W dokumentacji Kotlin o idiomach nawet na przykladzie Gson widac, ze JsonElement jest uzywany jako „uniwersalny nosnik struktury JSON”, ktory potem mozna zamieniac na potrzebny typ. To ta sama idea, co u nas z kotlinx.serialization.json: najpierw struktura, potem (jesli trzeba) string.
Z XML jest podobnie: jesli nie uzywasz biblioteki, to „sklejanie stringow” staje sie prawie nieuniknione, ale wtedy szczegolnie wazne sa escapowanie i ostroznosc. I tak, to dokladnie ten moment, gdy program zaczyna sie mscic za „a, jakos to bedzie”.
9. Typowe bledy przy wyborze i uzywaniu JSON/XML
Blad nr 1: myslec, ze „jak sie sparsowalo, to znaczy, ze jest poprawne”.
I JSON, i XML moga byc syntaktycznie poprawne, a jednoczesnie zawierac wartosci bez sensu dla twojego programu. Na przyklad id = -10, puste imie, kwota wydatku 0 tam, gdzie nie powinna byc zerowa. Dlatego warstwa walidacji to nie „dodatkowa opcja”, tylko obowiazkowa czesc projektu: parsowanie odpowiada za forme, walidacja — za sens.
Blad nr 2: mylic „nie ma pola” z „pole jest, ale jest puste/zerowe”.
W JSON widzielismy juz roznice miedzy „pola nie ma” a „wartosc to null”. W XML analogiczny problem objawia sie inaczej: elementu moze nie byc, element moze byc pusty (<name></name>), element moze byc samozamykajacy (<name/>) — i to czesto oznacza rozne rzeczy. Jesli nie uzgodnic semantyki, dostaniesz mase dziwnych if.
Blad nr 3: oczekiwac, ze w XML typy przyjda „same”.
W JSON typy prymitywow sa widoczne od razu, a w XML prawie wszystko wyglada jak string. Poczatkujacy czesto pisza kod tak, jakby <age>21</age> bylo „juz liczba”, i porownuja jak string, albo zapominaja obsluzyc nieliczbowa wartosc. W XML trzeba z gory planowac bezpieczne konwersje i komunikaty o bledach, inaczej dane „zepsuja” twoj scenariusz w najmniej odpowiednim momencie.
Blad nr 4: niekontrolowane mieszanie atrybutow i elementow w XML.
Gdy jedni programisci trzymaja id w atrybucie (<user id="1">), a inni — w elemencie (<id>1</id>), dostajesz dwa „podformaty” w jednym formacie. Potem ktos pisze parser pod jeden wariant, a dane przychodza w drugim. Rezultat to bug wygladajacy jak „czasem nie dziala”, czyli najbardziej irytujacy typ buga.
Blad nr 5: budowac JSON/XML „konkatenacja stringow”, a potem dziwic sie, ze wszystko sie sypie.
Sklejanie stringow prawie gwarantuje bledy: zapomniales przecinka w JSON, nie zescapowales & albo < w XML, przypadkiem dodales dodatkowy znak nowej linii i tyle — parser niezadowolony, uzytkownik niezadowolony, ty niezadowolony z zycia. Jesli masz mozliwosc budowac JSON jako drzewo — rob tak. Dla XML tez warto dazyc do budowania strukturalnego, ale w ramach tego wykladu przynajmniej utrwalamy problem: skladanie tekstowe jest kruche.
Blad nr 6: wybierac format „bo go lubie”, a nie dlatego, ze pasuje do zadania.
Lubienie JSON jest OK. Lubienie XML tez jest OK (byle nie stalo sie to twoja jedyna cecha charakteru). Ale format wybiera sie po kryteriach: co wymusza integracja, co latwiej walidowac, gdzie wazna jest kolejnosc, gdzie potrzebna jest zwiezlosc, kto bedzie czytac dane — czlowiek czy program. Jesli kryteria nie sa omowione, decyzja zwykle wychodzi przypadkowa, a cena przypadkowosci to czas zespolu i jakosc produktu.
GO TO FULL VERSION