CodeGym /Kursy /Kotlin SELF /JSON vs XML — praktyczne kryteria wyboru formatu

JSON vs XML — praktyczne kryteria wyboru formatu

Kotlin SELF
Poziom 49, Lekcja 4
Dostępny

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 &amp; albo &lt; 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.

1
Zadanie
Kotlin SELF, poziom 49, lekcja 4
Niedostępne
Pomoc formatu
Pomoc formatu
1
Zadanie
Kotlin SELF, poziom 49, lekcja 4
Niedostępne
Podwójna serializacja
Podwójna serializacja
1
Zadanie
Kotlin SELF, poziom 49, lekcja 4
Niedostępne
Wybór formatu
Wybór formatu
1
Zadanie
Kotlin SELF, poziom 49, lekcja 4
Niedostępne
Wiek z tekstu
Wiek z tekstu
1
Ankieta/quiz
JSON bez schematu, poziom 49, lekcja 4
Niedostępny
JSON bez schematu
JSON bez schematu
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION