๐ JSON APIs in Jakarta EE: Your Magic Toolkit for Data!
Imagine you have a magical toy box that can hold any toy in the world. But hereโs the catch โ you need special tools to put toys in, take them out, find specific ones, and even change them while theyโre inside!
Thatโs exactly what JSON APIs are in Jakarta EE. JSON is like the universal language that all apps speak, and these APIs are your special tools to work with it!
๐งฉ The Six Magic Tools
Think of these six tools like a superheroโs utility belt:
| Tool | What It Does | Likeโฆ |
|---|---|---|
| JSON Object Model | Build & read JSON like LEGO | Building blocks |
| JSON Streaming | Read/write one piece at a time | A conveyor belt |
| JSON Pointer | Point to exact spots | GPS for data |
| JSON Patch | Make precise changes | Surgical edits |
| JSON-B Annotations | Auto-convert Java โ JSON | Magic translator |
| Polymorphic Handling | Handle different types | Shape-shifter |
๐๏ธ JSON Object Model: Building with LEGO Blocks
The Story
Imagine youโre building a LEGO house. You pick up each brick, connect them together, and when youโre done โ you have a complete structure you can look at from any angle!
JSON Object Model works the same way. You build your JSON piece by piece, and once itโs built, you can explore it however you want.
Building JSON (Like stacking LEGO)
JsonObject person = Json.createObjectBuilder()
.add("name", "Maya")
.add("age", 10)
.add("hobbies", Json.createArrayBuilder()
.add("reading")
.add("coding"))
.build();
This creates:
{
"name": "Maya",
"age": 10,
"hobbies": ["reading", "coding"]
}
Reading JSON (Like examining your LEGO creation)
String name = person.getString("name");
int age = person.getInt("age");
JsonArray hobbies = person.getJsonArray("hobbies");
๐ก Key Insight
The Object Model loads everything into memory at once. Great for small data, but be careful with huge files!
graph TD A["JSON String"] --> B["Parse All At Once"] B --> C["JsonObject in Memory"] C --> D["Read Any Part Instantly"]
๐ญ JSON Streaming API: The Conveyor Belt
The Story
Picture a factory conveyor belt. Items come one by one. You check each item as it passes, decide what to do, then move to the next. You never see the whole pile at once!
Streaming API reads or writes JSON piece by piece โ perfect for huge data that wonโt fit in memory.
Two Parts to Streaming
1. JsonParser (Reading) โ Like watching items on a conveyor belt:
JsonParser parser = Json.createParser(inputStream);
while (parser.hasNext()) {
Event event = parser.next();
if (event == Event.KEY_NAME) {
String key = parser.getString();
}
if (event == Event.VALUE_STRING) {
String value = parser.getString();
}
}
2. JsonGenerator (Writing) โ Like placing items on a conveyor belt:
JsonGenerator gen = Json.createGenerator(outputStream);
gen.writeStartObject()
.write("name", "Leo")
.write("score", 100)
.writeEnd();
gen.close();
๐ฏ When to Use What?
| Use Object Model Whenโฆ | Use Streaming Whenโฆ |
|---|---|
| Data is small | Data is huge (GB+) |
| Need random access | Only need one pass |
| Structure is complex | Memory is limited |
graph TD A["Small JSON"] --> B["Object Model"] C["Huge JSON"] --> D["Streaming API"] D --> E["Read piece by piece"] D --> F["Low memory usage"]
๐ฏ JSON Pointer: GPS for Your Data
The Story
Youโre in a huge library with millions of books. Instead of wandering around, you use a GPS that tells you: โGo to Floor 3, Section B, Shelf 5, Book 12.โ
JSON Pointer is that GPS. It takes you directly to any piece of data!
The Syntax
A JSON Pointer is a string starting with /, with each part separated by /:
/users/0/name
โ โ โ
| | โโโ the "name" field
| โโโโโโโ first item (index 0)
โโโโโโโโโโโโ the "users" array
Example in Action
Given this JSON:
{
"store": {
"books": [
{"title": "Adventure Time", "price": 12},
{"title": "Space Quest", "price": 15}
]
}
}
JsonPointer pointer = Json.createPointer("/store/books/1/title");
JsonString title = (JsonString) pointer.getValue(jsonObject);
// Returns: "Space Quest"
๐จ Special Characters
| Character | Escape As |
|---|---|
/ |
~1 |
~ |
~0 |
// To access key "a/b" use:
JsonPointer p = Json.createPointer("/keys/a~1b");
โ๏ธ JSON Patch: Surgical Edits
The Story
Imagine you have a photo and want to change just the eye color, not redraw the whole picture. You use a tiny, precise brush!
JSON Patch lets you describe exactly what changes to make without sending the whole document. Super efficient!
The Operations
There are 6 operations you can do:
| Operation | What It Does | Example |
|---|---|---|
add |
Insert new value | Add a new field |
remove |
Delete something | Remove a field |
replace |
Swap values | Change a value |
move |
Relocate data | Move field A to B |
copy |
Duplicate data | Copy field A to B |
test |
Check a value | Verify before changing |
Patch in Action
JsonPatch patch = Json.createPatchBuilder()
.add("/nickname", "Max")
.replace("/age", 11)
.remove("/oldField")
.build();
JsonObject updated = patch.apply(original);
The Patch Format (RFC 6902)
[
{"op": "add", "path": "/email", "value": "max@example.com"},
{"op": "replace", "path": "/score", "value": 200},
{"op": "test", "path": "/active", "value": true}
]
๐ก Why test Matters
The test operation ensures a value is what you expect before making changes. If the test fails, the whole patch is rejected!
graph TD A["Original JSON"] --> B["Apply Patch"] B --> C{Test Passes?} C -->|Yes| D["Apply Changes"] C -->|No| E["Reject All"]
๐ JSON-B Annotations: The Magic Translator
The Story
Imagine you have a translator who instantly converts English to French and back. You just speak normally, and they handle everything!
JSON-B (JSON Binding) automatically converts Java objects to JSON and back. Annotations customize how the translation works!
Key Annotations
@JsonbProperty โ Rename fields:
public class Hero {
@JsonbProperty("hero_name")
private String name; // becomes "hero_name" in JSON
}
@JsonbTransient โ Skip fields:
public class User {
private String name;
@JsonbTransient
private String password; // Never in JSON!
}
@JsonbDateFormat โ Format dates:
public class Event {
@JsonbDateFormat("yyyy-MM-dd")
private LocalDate eventDate;
}
@JsonbNumberFormat โ Format numbers:
public class Product {
@JsonbNumberFormat("#,##0.00")
private double price; // "1,234.56"
}
Quick Conversion
Jsonb jsonb = JsonbBuilder.create();
// Java โ JSON
String json = jsonb.toJson(myObject);
// JSON โ Java
MyClass obj = jsonb.fromJson(json, MyClass.class);
graph TD A["Java Object"] -->|toJson| B["JSON String"] B -->|fromJson| A
๐จ More Annotations
| Annotation | Purpose |
|---|---|
@JsonbCreator |
Custom constructor |
@JsonbNillable |
Include null fields |
@JsonbPropertyOrder |
Control field order |
@JsonbVisibility |
Control access |
๐ฆ Polymorphic Type Handling: The Shape-Shifter
The Story
Imagine a magical pet that can be a dog, cat, or bird. When you save it to a file and open it later, how do you know which animal it is?
Polymorphic handling solves this! It stores a type hint so you know exactly what type to recreate.
The Challenge
abstract class Animal { }
class Dog extends Animal { String bark; }
class Cat extends Animal { String meow; }
List<Animal> pets = Arrays.asList(new Dog(), new Cat());
How does JSON know a Dog from a Cat when reading back?
Solutions
1. Type Property (Most Common)
Add a field that tells the type:
[
{"@type": "Dog", "bark": "woof"},
{"@type": "Cat", "meow": "purr"}
]
2. Custom Deserializer
public class AnimalDeserializer
implements JsonbDeserializer<Animal> {
@Override
public Animal deserialize(JsonParser parser,
DeserializationContext ctx, Type type) {
JsonObject obj = parser.getObject();
String animalType = obj.getString("@type");
if ("Dog".equals(animalType)) {
return ctx.deserialize(Dog.class, parser);
} else {
return ctx.deserialize(Cat.class, parser);
}
}
}
3. Using @JsonbTypeInfo (Jakarta EE 10+)
@JsonbTypeInfo(
key = "@type",
value = {
@JsonbSubtype(alias = "dog", type = Dog.class),
@JsonbSubtype(alias = "cat", type = Cat.class)
}
)
public abstract class Animal { }
graph TD A["JSON with @type"] --> B{Check Type} B -->|dog| C["Create Dog"] B -->|cat| D["Create Cat"] B -->|bird| E["Create Bird"]
๐ก Best Practices
- Use a consistent type key (
@type,_type,kind) - Keep type names simple (avoid full class names)
- Register all subtypes for validation
- Test round-trip (serialize then deserialize)
๐ฎ Putting It All Together
Hereโs how all six tools work in a real REST API:
graph TD A["Client Request"] --> B{What to do?} B -->|Read big file| C["Streaming API"] B -->|Build response| D["Object Model"] B -->|Convert Java| E["JSON-B"] B -->|Find value| F["JSON Pointer"] B -->|Update part| G["JSON Patch"] B -->|Handle types| H["Polymorphic"]
Example: REST Endpoint
@Path("/users")
public class UserResource {
@GET
@Path("/{id}")
public User getUser(@PathParam("id") long id) {
// JSON-B auto-converts to JSON!
return userService.find(id);
}
@PATCH
@Path("/{id}")
public User patchUser(@PathParam("id") long id,
JsonPatch patch) {
JsonObject current = getCurrentAsJson(id);
JsonObject updated = patch.apply(current);
return saveFromJson(updated);
}
}
๐ Quick Summary
| Tool | One-Liner |
|---|---|
| Object Model | Build/read JSON like LEGO blocks |
| Streaming | Handle huge data piece by piece |
| Pointer | GPS to find exact data location |
| Patch | Make surgical updates |
| JSON-B | Magic Java โ JSON conversion |
| Polymorphic | Handle different types smartly |
๐ You Did It!
You now understand the six superpowers of JSON APIs in Jakarta EE! Whether youโre building tiny responses or handling massive data streams, you have the right tool for every job.
Remember:
- Small data? โ Object Model
- Huge data? โ Streaming
- Find something? โ Pointer
- Change something? โ Patch
- Convert objects? โ JSON-B
- Multiple types? โ Polymorphic
Now go build something amazing! ๐
