JavaRush /Courses /Kotlin SELF /JSON vs XML — practical criteria for choosing a format

JSON vs XML — practical criteria for choosing a format

Kotlin SELF
Level 49, Lesson 4
Available

1. The mental model of JSON and XML

To be honest, a developer rarely wakes up in the morning thinking: “Why don’t I compare JSON and XML today, before my coffee gets cold?” Usually, the format choice happens because someone else has already made half the decision for you: an external service returns XML, a legacy corporate API requires SOAP, accounting sends you a “magnificent” XML file, while your project, on the contrary, wants a simple report in JSON. And then you’re standing between two worlds: the compact “data as structure” world (JSON) and the more “document-like” world of tags (XML).

It’s important to understand: JSON and XML are not “better/worse”; they are different data representation models. And if you pick a format without criteria, you can easily end up in a situation where the data seems to be transmitted, but every next development step starts to feel like a quest: “guess where we lost the types / order / nesting / escaping.”

To choose a format consciously, it helps to first agree on how we mentally “see” data in each format. It’s like choosing a container for food: soup in a plastic bag is technically possible, but you’re not obligated to suffer like that. JSON and XML answer the question “what is a data structure?” differently, and that directly affects how easy it will be for us to read, validate, and generate this data in Kotlin.

JSON as a tree of values

JSON is a tree where nodes have four main types: object (key-value pairs), array (an ordered list), primitive (string/number/boolean), and null. In previous lectures, we literally worked with this as a JsonElement tree, and that feeling is very accurate: each node is a value that has a type, and the type can be checked.

Practical effect: if you see true or 123 in JSON, you already understand that it’s a boolean and a number (even though sometimes you still have to convert from a string if the data is “dirty”).

XML as a tree of document elements

XML is also a tree, but the nodes are elements (tags) with names, attributes, and text content. And here the philosophy begins: historically, XML grew as a document format where markup, nesting, and the meaning of tags matter, not the “type of the value” as such.

Practical effect: almost everything in XML is string-like by nature (text inside an element or an attribute value). You add typing via “convention”: for example, <age>21</age> is a number only because you decided so and your code parsed it that way.

3. Data vs document

In real projects, the most useful question sounds like this: are you transmitting data (a structure for a program) or a document (a text with markup where elements, attributes, order, and sometimes mixed content matter)? This isn’t “pure theory” — it’s a quick way to stop arguing in the style of “JSON is trendy, XML is old” and start choosing the right format for the job.

JSON usually wins when you describe “here’s an object, it has fields, fields have types, sometimes arrays.” This is a typical format for APIs, configs, reports, and data exchange between services.

XML often wins when you describe “here’s a document, it has a structure, elements, metadata in attributes, sometimes order matters, sometimes namespaces matter.” This is a typical format for document workflows, some integrations, legacy standards, and in general a world where “tags” matter more than “types.”

4. Comparison by criteria

Below is a practical criteria table. It’s not “the ultimate truth,” but it’s a good checklist that really helps you make a decision and not regret it later.

Criterion JSON XML What it means for Kotlin code
Readability for “data” Usually simpler and shorter Usually more verbose JSON is quicker to read and easier to generate
Primitive “types” Types are visible (true, 123, "text", null) Everything looks like text With JSON there’s less “type guessing”; with XML parsing is almost always manual/rule-based
Arrays/lists Natural [...] Done with repeating tags In JSON it’s easier to express “a list of same-type things”
Attributes as a separate concept No (everything is fields) Has attributes XML conveniently stores “metadata” right next to the element
Order Matters in arrays; usually doesn’t in objects Element order is often treated as important If order is meaningful as part of the semantics, XML may feel more natural
Comments Strict JSON doesn’t have them XML has comments For “human” configs XML can sometimes be more convenient (but it’s a double-edged sword)
Schemas/validation Approaches exist, but often “by convention” Historically strong schema ecosystem If you have strict standards and contracts, the XML world is often more “regulated”
Size/noise Usually smaller Usually larger For logs/network responses, JSON is better in terms of payload size
Integrations Very common in modern APIs Very common in legacy/corporate standards The choice is often dictated by the external environment

A small observation from real life: Kotlin tools and the ecosystem like JSON because it’s a convenient format for machine reports. For example, the migration documentation for K2 mentions storing build reports in JSON format, meaning JSON is genuinely used as a “machine-readable report.”

5. The same meaning in JSON and XML

Comparing formats without examples is like explaining swimming from a book: technically possible, but better not. Let’s take the same meaning and write it in two ways. Suppose we have a user and their tags.

Example data

  • user: id=1, name="Alice"
  • tags: ["kotlin", "json"]

JSON version

fun main() {
    val json = """{"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}"""
    println(json) // {"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}
}

Here, an array is an array, numbers are numbers, strings are strings. No surprises: the structure is very close to Kotlin data structures (Map/List/primitives).

XML version

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 looks more “document-like”: lots of tags, an explicit hierarchy, repeating elements for a list. And note: the number 1 here is text "<id>1</id>". Only your parser (or your validation) will know that it’s an Int.

6. Typing and attributes

Typing and why everything in XML suddenly becomes a string

If you’ve ever caught a bug like “why is age 21 compared as a string and it turns out 9 is ‘greater’ than 21,” then you already intuitively understand why typing matters. JSON helps because primitives are typed at the format level. XML leaves typing to conventions and to the code that reads the document.

Practical effect #1: validation is needed in any format

Both JSON and XML can be “syntactically correct” yet “logically garbage.” We already discussed this for JSON: parseToJsonElement() only answers “is this valid JSON,” not “do these values work for us.” With XML it’s similar: even if XML is well-formed, it can contain empty fields, invalid ranges, unexpected elements.

Practical effect #2: in XML you more often write conversions

In XML you almost always do String -> Int? / String -> Boolean? yourself. In JSON this also happens (especially if the data is “dirty”), but by default the types are at least closer to what you expect.

XML attributes: a strength and a source of endless arguments

Attributes are one of the reasons XML is still alive and not planning to “retire to a museum of ancient technologies.” Attributes let you store metadata right on the element. That’s convenient, but it also creates “religious wars”: what should be an attribute, and what should be an element?

Let’s compare two variants of the same meaning:

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

In JSON there’s almost no such choice: you would just do "id": 1. And that’s a plus: fewer options means less confusion in both data and code.

7. Where JSON and XML fit in Kotlin projects

Where JSON is especially good in our course

We’ve already built reports, aggregated data, built pipelines like “took a collection → calculated → produced a result.” And for these tasks JSON is an almost ideal “container for the result”: easy to assemble manually, easy to save, easy to send as a structure.

By the way, in the Kotlin ecosystem, serialization isn’t “exotic”; it’s a stable tool: the Kotlin components stability table explicitly mentions kotlinx-serialization as a stable library.

Mini-example: a JSON report for our expense tracker

Let’s imagine that in our practical application (say, “Expense Tracker”) we calculate the total expenses and want to produce a report.

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

Notice how naturally this maps onto Kotlin collections: we computed numbers and put them into JSON as numbers, without losing types “along the way.”

Where XML might be more appropriate

Sometimes choosing “XML” isn’t because you’re craving adventure; it’s because you’re integrating with a world where XML is already the standard. That can be documents, integration formats, industry standards, or sometimes just “that’s how it has always been.”

In the Kotlin/JVM world, XML often comes together with Java libraries and standard APIs. Even in Kotlin change logs you can find mentions of infrastructure tied to org.w3c (a family of interfaces historically associated with the DOM model for XML).

But the key point is this: with XML you more often need to agree upfront on “representation rules.” For example, how do we encode a list: with repeating tags? with a separate container <items>? What do we name elements? This isn’t bad; it’s just a different style.

8. Validation and generation: how not to break a project

The format is not the same as business rules

Here’s an important point that often breaks projects: people think that if the format is chosen “correctly,” then validation is no longer needed. Unfortunately, a format is only syntax and basic structure.

We already built an Ok/Error layer for JSON validation. And here’s a good habit: regardless of whether you have JSON or XML, keep the same architecture in mind:

flowchart TD
    A["Text (JSON or XML)"] --> B["Parsing: 'Is this even a valid format?'"]
    B --> C["Validation: 'Does the data meet the application rules?'"]
    C --> D["Domain Model / Calculations / Reports"]

This diagram saves you from the typical pain: “it sometimes crashes in prod,” because someone sent a valid file, but with an empty id or a negative sum.

A quick format selection algorithm

Sometimes you want a simple rule so you don’t argue endlessly. Let this be not a rule, but a short decision-making algorithm that works quite well for most learning and practical tasks.

First, answer the question: is the data structure stable or not? If the structure is unstable, JSON gives you an excellent tool — the JsonElement tree and safe extraction.

Next, ask: do you care more about “data as object/array” or “document as markup”? If data matters more, JSON is chosen more often. If document-ness, standards, and compatibility with existing XML contracts matter more — XML.

And finally, the most real criterion: who is your “integration neighbor”? If the outside world returns XML, you won’t defeat reality with a motivational speech about JSON — you will read XML. If the outside world speaks JSON — you will work with JSON.

One small nuance: don’t concatenate strings

This is a small piece of advice from the category “everyone steps on rakes, but you can at least wear soft slippers.” If you generate JSON, try to build it as a structure (buildJsonObject, buildJsonArray), not by concatenating strings.

In the Kotlin documentation on idioms, even using Gson as an example, you can see that JsonElement is used as a “universal carrier of JSON structure,” which can later be converted into the needed type. It’s the same idea we have with kotlinx.serialization.json: structure first, then (if needed) a string.

With XML it’s a similar story: if you don’t use a library, “string concatenation” becomes almost inevitable, but then escaping and carefulness become especially important. And yes, this is exactly the moment when the program starts taking revenge for “good enough.”

9. Common mistakes when choosing and using JSON/XML

Mistake #1: thinking “if it parsed, it must be correct.”
Both JSON and XML can be syntactically valid while still containing values that are meaningless for your program. For example, id = -10, an empty name, an expense amount of 0 where it must not be zero. That’s why a validation layer is not an “optional extra”; it’s a mandatory part of design: parsing handles form, validation handles meaning.

Mistake #2: confusing “no field” with “the field exists but is empty/null.”
In JSON we’ve already seen the difference between “no field” and a value of null. In XML the same problem shows up differently: an element may be absent, it may be empty (<name></name>), it may be self-closing (<name/>), and those often mean different things. If you don’t agree on semantics, you’ll end up with a bunch of weird if.

Mistake #3: expecting types to “arrive on their own” in XML.
In JSON primitive types are visible immediately, while in XML almost everything looks like a string. Beginners often write code as if <age>21</age> is “already a number,” and compare it as a string, or forget to handle a non-numeric value. With XML you need to plan safe conversions and error messages in advance, otherwise the data will “break” your flow at the worst possible moment.

Mistake #4: uncontrolled mixing of attributes and elements in XML.
When some developers put id in an attribute (<user id="1">), and others put it in an element (<id>1</id>), you end up with two “sub-formats” inside one format. Then someone writes a parser for one variant, but the data comes in the other. The result is a bug that looks like “sometimes it doesn’t work,” i.e., the most annoying kind of bug.

Mistake #5: building JSON/XML via string concatenation and then being surprised that everything breaks.
String concatenation almost guarantees mistakes: you forgot a comma in JSON, didn’t escape &amp; or &lt; in XML, accidentally added an extra newline, and that’s it — the parser is unhappy, the user is unhappy, and you’re unhappy with life. If you can build JSON as a tree, do that. For XML it’s also better to aim for structural building, but within this lecture we at least fix the problem: string-based building is fragile.

Mistake #6: choosing a format “because you like it,” not because it fits the problem.
Liking JSON is fine. Liking XML is also fine (as long as it doesn’t become your only personality trait). But you choose a format by criteria: what the integration dictates, what’s easier to validate, where order matters, where compactness is needed, who will read the data — a human or a program. If the criteria aren’t discussed, the decision usually ends up being random, and the cost of randomness is the team’s time and product quality.

1
Task
Kotlin SELF, level 49, lesson 4
Locked
Format help
Format help
1
Task
Kotlin SELF, level 49, lesson 4
Locked
Dual serialization
Dual serialization
1
Task
Kotlin SELF, level 49, lesson 4
Locked
Choosing a format
Choosing a format
1
Task
Kotlin SELF, level 49, lesson 4
Locked
Age from text
Age from text
1
Survey/quiz
Schema-less JSON, 49 level, 4 lesson
Unavailable
Schema-less JSON
Schema-less JSON
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION