Skip to main content

    Lesson 36 • Advanced

    JSON & XML Processing

    By the end of this lesson you'll be able to turn Java objects into JSON and back with Jackson, walk dynamic JSON with the tree model, and safely read XML — the two data formats that power nearly every API and config file you'll ever touch.

    What You'll Learn

    • Convert Java objects to JSON with Jackson's writeValueAsString
    • Parse JSON into objects with ObjectMapper.readValue
    • Rename and hide fields using @JsonProperty and @JsonIgnore
    • Navigate dynamic JSON with the JsonNode tree model
    • Know when to reach for Gson instead of Jackson
    • Read XML safely with DOM, and know DOM vs SAX vs JAXB

    Before You Start

    You should be comfortable with REST APIs (JSON is the standard API format) and Annotations (Jackson uses @JsonProperty, @JsonIgnore, and friends). A basic grasp of maps and lists helps too.

    1️⃣ What Are JSON and XML? (Plain English)

    JSON (JavaScript Object Notation) and XML (eXtensible Markup Language) are both ways to write data as plain text so two programs can exchange it. Your Java program can't send a live object across the internet, so it serializes the object to text, sends the text, and the other side deserializes it back into an object.

    💡 Analogy: Think of flat-pack furniture. The object in memory is the assembled chair. To ship it, you flatten it into a labelled box (serialize to JSON/XML). At the other end someone reads the labels and rebuilds the chair (deserialize). JSON is the lean, modern box; XML is the older box with more packaging and stricter labels.

    Here's the same data in both formats so you can feel the difference:

    JSON

    {
      "name": "Alice",
      "age": 30,
      "active": true
    }

    XML

    <user>
      <name>Alice</name>
      <age>30</age>
      <active>true</active>
    </user>
    FeatureJSONXML
    SyntaxLightweight, key-valueVerbose, tag-based
    Data typesString, number, bool, arrayEverything is text
    Best forREST APIs, configs, NoSQLEnterprise, SOAP, documents
    Java libraryJackson, GsonJAXB, DOM, SAX, StAX

    For a brand-new project JSON wins almost every time. XML still shows up in SOAP web services, Maven pom.xml files, and older enterprise systems — so it's worth knowing both.

    2️⃣ Jackson ObjectMapper — JSON ↔ Objects

    Jackson is the most popular JSON library for Java, and Spring Boot uses it automatically. The one class you need is ObjectMapper. It does two jobs: writeValueAsString(obj) turns an object into JSON text, and readValue(json, Type.class) turns JSON text back into an object.

    You map JSON onto a POJO — a plain class with fields. Two annotations control the mapping: @JsonProperty("key") renames a field in the JSON (handy when the API uses snake_case but you want camelCase in Java), and @JsonIgnore keeps a field out of the JSON entirely (perfect for passwords).

    Read every comment in this worked example, then run it:

    Worked Example: ObjectMapper round-trip
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class Main {
        // A POJO = "Plain Old Java Object": a simple class with fields.
        // Jackson reads/writes JSON straight onto these fields.
        static class User {
            public String name;
            public int age;
            // @JsonProperty renames the JSON key. The field stays camelCase in Java,
            // the JSON shows "is_active" (snake_case, the common API style).
            @JsonProperty("is_active") public boolean isActive;
            // @JsonIgnore keeps a field OUT of the JSON (e.g. a secret/password).
            @JsonIgnore public String password;
        }
    
        public static void main(String[] args) throws Exception {
            // ObjectMapper is the one object that does all the conversion.
            ObjectMapper mapper = new ObjectMapper();
    
            // 1) Serialize: Java object -> JSON text (writeValueAsString)
            User u = new User();
            u.name = "Alice"; u.age = 30; u.isActive = true; u.password = "secret123";
            String json = mapper.writeValueAsString(u);
            System.out.println("Serialized: " + json);   // password is omitted
    
            // 2) Deserialize: JSON text -> Java object (readValue)
            String input = "{\"name\":\"Bob\",\"age\":25,\"is_active\":false}";
            User parsed = mapper.readValue(input, User.class);
            System.out.println("name:   " + parsed.name);     // Bob
            System.out.println("age:    " + parsed.age);      // 25
            System.out.println("active: " + parsed.isActive); // false
        }
    }
    Output
    Serialized: {"name":"Alice","age":30,"is_active":true}
    name:   Bob
    age:    25
    active: false
    Jackson is a third-party library. Add com.fasterxml.jackson.core:jackson-databind to your Maven/Gradle build, then run with your local JDK.

    🎯 Your Turn #1 — map a POJO

    Fill in the three blanks (___): the annotation that renames inStock, the serialize method, and the deserialize method. The expected output is in the comments so you can self-check.

    Your Turn: finish the conversions
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class Main {
        // 🎯 YOUR TURN — finish the POJO and the two conversions
    
        static class Product {
            public String name;
            public double price;
            // 1) The JSON uses the key "in_stock" but you want a Java field
            //    called inStock. Add the annotation that renames it.
            ___ public boolean inStock;   // 👉 @JsonProperty("in_stock")
        }
    
        public static void main(String[] args) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
    
            Product p = new Product();
            p.name = "Mug"; p.price = 9.99; p.inStock = true;
    
            // 2) Serialize p to a JSON string (Object -> JSON)
            String json = mapper.___(p);          // 👉 writeValueAsString
            System.out.println(json);
    
            // 3) Deserialize back into a Product (JSON -> Object)
            Product copy = mapper.___(json, Product.class);  // 👉 readValue
            System.out.println("Name: " + copy.name + ", stock: " + copy.inStock);
    
            // ✅ Expected output:
            // {"name":"Mug","price":9.99,"in_stock":true}
            // Name: Mug, stock: true
        }
    }
    Fill in each ___ using the hint on its line, then run with jackson-databind on your JDK.

    3️⃣ The Tree Model — JSON Without POJOs

    Sometimes you only need one or two values from a big response, and writing matching classes would be overkill. Use the tree model: mapper.readTree(json) parses the JSON into a JsonNode you can walk like a document.

    mapper.readValue(json, Order.class) — POJO mapping (typed, compile-time safe)

    mapper.readTree(json) — tree model (dynamic, no class needed)

    node.get("field") — step into an object or array

    node.asText() / asInt() / asDouble() — read a leaf value

    A JsonNode that holds an array is iterable, so you can loop over it with a normal for loop.

    Worked Example: tree model navigation
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            // When you don't want to write matching POJOs, navigate the JSON as a
            // tree. Text block (Java 15+) keeps the JSON readable.
            String orderJson = """
                {
                  "id": "ORD-001",
                  "customer": { "name": "Alice", "tier": "Gold" },
                  "items": [
                    { "product": "Laptop", "quantity": 1, "price": 1299.99 },
                    { "product": "Hub",    "quantity": 2, "price": 49.99 }
                  ]
                }
                """;
    
            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(orderJson);   // parse into a tree
    
            // .get("key") steps into an object; .asText()/.asInt()/.asDouble() read values.
            System.out.println("Order: " + root.get("id").asText());          // ORD-001
            System.out.println("Tier:  " + root.get("customer").get("tier").asText()); // Gold
            System.out.println("Items: " + root.get("items").size());         // 2
    
            // A JsonNode array is iterable — loop it like a List.
            double total = 0;
            for (JsonNode item : root.get("items")) {
                double line = item.get("quantity").asInt() * item.get("price").asDouble();
                total += line;
            }
            System.out.printf("Total: $%.2f%n", total);   // Total: $1399.97
        }
    }
    Output
    Order: ORD-001
    Tier:  Gold
    Items: 2
    Total: $1399.97
    Uses Jackson's tree model and Java text blocks (Java 15+). Add jackson-databind to your build and run with a local JDK.

    🎯 Your Turn #2 — read from a tree

    No POJO this time. Fill in the three blanks to parse the JSON, read the top-level city as text, and reach into the nested weather.temp as an int.

    Your Turn: navigate the JsonNode
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            // 🎯 YOUR TURN — read values out of the tree (no POJO)
    
            String json = "{\"city\":\"Paris\",\"weather\":{\"temp\":21,\"sky\":\"clear\"}}";
    
            ObjectMapper mapper = new ObjectMapper();
    
            // 1) Parse the JSON text into a tree
            JsonNode root = mapper.___(json);     // 👉 readTree
    
            // 2) Read the top-level "city" value as text
            String city = root.get("city").___;   // 👉 asText()
    
            // 3) Step into "weather" then read "temp" as an int
            int temp = root.get("weather").get("temp").___;  // 👉 asInt()
    
            System.out.println(city + ": " + temp + "C");
    
            // ✅ Expected output:
            // Paris: 21C
        }
    }
    Fill in each ___ using the hint on its line, then run with jackson-databind on your JDK.

    4️⃣ A Note on Gson

    Gson is Google's JSON library and the main alternative to Jackson. The API is even smaller — no ObjectMapper to configure, just a Gson object:

    Gson gson = new Gson();
    String json = gson.toJson(user);              // Object -> JSON
    User back   = gson.fromJson(json, User.class); // JSON -> Object

    Gson is great for small apps or quick scripts where you want zero setup. For anything bigger — especially Spring Boot, which wires up Jackson for you — prefer Jackson for its richer annotations, streaming parser, and ecosystem. Pick one per project and stay consistent.

    5️⃣ Parsing XML — DOM, SAX, and JAXB

    Java ships with several XML tools. The three you'll meet most:

    • DOM — loads the whole document into a tree in memory. Easy to navigate and edit, but a huge file can run you out of memory.
    • SAX (and its cousin StAX) — streams the document, firing events as it reads. Memory stays constant for any file size, but you can only move forwards and can't edit.
    • JAXB — maps XML to/from Java objects with annotations (@XmlRootElement), the XML equivalent of Jackson. A Marshaller writes objects to XML; an Unmarshaller reads XML into objects.
    Rule of thumb: small document you need to walk → DOM. Large file you only read once → SAX/StAX. You control both sides and want typed objects → JAXB.
    Worked Example: read XML safely with DOM
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.DocumentBuilder;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import java.io.StringReader;
    import org.xml.sax.InputSource;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            String xml = "<book><title>Java Basics</title><pages>320</pages></book>";
    
            // DOM: loads the WHOLE document into a tree in memory. Easy to navigate,
            // but heavy for big files. (SAX/StAX stream instead — see the notes.)
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    
            // SECURITY: turn off external entities to block XXE attacks.
            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            factory.setExpandEntityReferences(false);
    
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(xml)));
    
            Element rootEl = doc.getDocumentElement();   // <book>
            String title = rootEl.getElementsByTagName("title").item(0).getTextContent();
            String pages = rootEl.getElementsByTagName("pages").item(0).getTextContent();
    
            System.out.println("Root tag: " + rootEl.getTagName());  // book
            System.out.println("Title:    " + title);                // Java Basics
            System.out.println("Pages:    " + pages);                // 320
        }
    }
    Output
    Root tag: book
    Title:    Java Basics
    Pages:    320
    DOM and SAX are built into the JDK — no extra dependency needed. Run this with your local JDK.

    🧩 Mini-Challenge — average JSON scores

    Support is faded now: the starter only has a comment outline. Parse the scores array with the tree model, sum and count the values, and print the average to one decimal place.

    Mini-Challenge: write it yourself
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            // 🎯 MINI-CHALLENGE: average a JSON array of scores
            // The JSON below holds a "scores" array of numbers.
            // 1. Parse it with ObjectMapper.readTree(...)
            // 2. Loop over root.get("scores") (a JsonNode is iterable)
            // 3. Add up each value with .asInt() and count them
            // 4. Print "Average: X.X" to one decimal place (%.1f)
            //
            // ✅ Expected (scores 90, 80, 70): Average: 80.0
    
            String json = "{\"scores\":[90,80,70]}";
    
            // your code here
        }
    }
    Follow the numbered steps in the comments. Add jackson-databind to your build and run with a local JDK.

    Common Errors (and the fix)

    • UnrecognizedPropertyException: Unrecognized field "..." — the JSON has a key your POJO doesn't declare. Fix: mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) or annotate the class with @JsonIgnoreProperties(ignoreUnknown = true).
    • InvalidDefinitionException: Cannot construct instance ... no default constructor — Jackson needs a no-argument constructor to build the object before setting fields. Fix: add a public no-arg constructor, or use @JsonCreator with @JsonProperty parameters.
    • InvalidFormatException: Cannot deserialize value of type LocalDate from String "..." — the date string doesn't match the expected format. Fix: register JavaTimeModule and annotate the field with @JsonFormat(pattern = "yyyy-MM-dd").
    • SAXParseException / OutOfMemoryError on big XML — DOM tried to load a giant file into memory. Fix: switch to SAX or StAX streaming for large documents.
    • XXE vulnerability — parser fetched a local file — you parsed untrusted XML with defaults. Fix: factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) and disable external entities before parsing.

    Pro Tips

    • 💡 Create one ObjectMapper and reuse it — it's thread-safe and expensive to build.
    • 💡 Use @JsonProperty("user_name") to bridge snake_case APIs and camelCase Java fields.
    • 💡 Reach for readTree when the shape is unknown or you need just a couple of fields.
    • 💡 For GB-sized files use Jackson's streaming JsonParser (or XML's SAX/StAX) — constant memory.

    📋 Quick Reference

    TaskCallNotes
    Object → JSONmapper.writeValueAsString(obj)Serialize
    JSON → Objectmapper.readValue(json, T.class)Needs a no-arg constructor
    JSON → treemapper.readTree(json)No POJO needed
    Rename field@JsonProperty("key")snake_case ↔ camelCase
    Hide field@JsonIgnoreOmit from JSON
    Gson (alt)gson.toJson / fromJsonLighter library
    XML → treebuilder.parse(source)DOM (small files)
    XML streamingSAXParser / XMLStreamReaderLarge files, read-only
    XML ↔ ObjectMarshaller / UnmarshallerJAXB

    ❓ Frequently Asked Questions

    Should I use Jackson or Gson for JSON in Java?

    Use Jackson for most projects — it is the industry standard, Spring Boot auto-configures it, and it has the richest annotation and streaming support. Gson (from Google) is a lighter alternative with a simpler API (new Gson().toJson(obj) / fromJson(json, T.class)); it is fine for small apps or when you want zero configuration. Both map POJOs to JSON; pick one per project and stay consistent.

    Why does Jackson throw 'no default constructor' when deserializing?

    Jackson creates the object first (calling a no-argument constructor) and then sets the fields. If your class only has a constructor that takes arguments, the default one disappears, so Jackson cannot instantiate it. Fix it by adding a public no-arg constructor, or annotate a constructor with @JsonCreator and its parameters with @JsonProperty so Jackson knows how to build the object.

    What is the difference between DOM and SAX for XML?

    DOM loads the entire XML document into a tree in memory, so you can navigate and edit it freely — but a large file can exhaust memory. SAX (and StAX) stream the document, firing events as they read, so memory stays constant no matter how big the file is — but you can only move forwards and cannot edit. Use DOM for small documents you need to traverse; use SAX/StAX for large files you only need to read once.

    How do I stop Jackson crashing when the JSON has extra fields?

    By default Jackson throws UnrecognizedPropertyException if the JSON contains a key your POJO does not declare. Disable that check with mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false), or annotate the class with @JsonIgnoreProperties(ignoreUnknown = true). This makes your code resilient when an API adds new fields later.

    What is an XXE attack and how do I prevent it?

    XXE (XML External Entity) is a vulnerability where malicious XML declares an external entity that makes your parser read local files or hit internal URLs. Prevent it by hardening the factory before parsing: set the feature 'http://apache.org/xml/features/disallow-doctype-decl' to true, and disable external general/parameter entities. Always do this for any XML that comes from outside your application.

    🎉 Lesson Complete!

    You can now move data in and out of Java with confidence: serialize and deserialize POJOs with Jackson's ObjectMapper, rename and hide fields with @JsonProperty / @JsonIgnore, walk dynamic JSON with the tree model, choose Gson when you want something lighter, and read XML safely while knowing DOM from SAX from JAXB.

    Next up: Unit Testing — write reliable tests with JUnit 5 and Mockito.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service