Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
254: Parsing and Generating JSON with Jackson
I see this a lot when developers first start integrating Java with REST APIs. You've got your POJO (Plain Old Java Object) looking perfect, your JSON string is valid, and yet your application crashes the second you try to map one to the other. Let's look at a snippet that looks correct on the surface but is actually a ticking time bomb.
public class UserProfile {
private String username;
private String email;
// I want to make sure my objects are always initialized with data
public UserProfile(String username, String email) {
this.username = username;
this.email = email;
}
public String getUsername() { return username; }
public String getEmail() { return email; }
}
// In the main logic...
ObjectMapper mapper = new ObjectMapper();
String json = "{\"username\": \"java_dev\", \"email\": \"dev@example.com\"}";
UserProfile profile = mapper.readValue(json, UserProfile.class);
// BOOM: InvalidDefinitionException
The "No Creators" Crash
If you run this, Jackson will throw an InvalidDefinitionException telling you it "cannot construct instance of UserProfile (no Creators, like default constructor, exist)".
Here is why: Jackson doesn't magically know how to map your JSON keys to your specific constructor arguments. By default, it uses reflection to create an empty instance of your class first and then uses the setter methods (or direct field access) to populate the data. Because you added a custom constructor, Java stopped providing the hidden, default no-args constructor. Jackson tries to find a way to instantiate the object, finds nothing it recognizes, and gives up.
Giving Jackson a Way In
The quickest fix is to explicitly add a default constructor. It doesn't have to do anything; it just needs to exist so Jackson can call it.
public class UserProfile {
private String username;
private String email;
// Jackson needs this!
public UserProfile() {}
public UserProfile(String username, String email) {
this.username = username;
this.email = email;
}
// Getters and setters...
}
Now, mapper.readValue() will work perfectly. I'll be honest: some people hate adding "empty" constructors because it feels like it breaks encapsulation. If you really want to keep your class immutable, you can use the @JsonCreator and @JsonProperty annotations on your constructor, but for 90% of business applications, the default constructor is the industry standard path of least resistance.
Turning Java Objects Back into JSON
Generating JSON (serialization) is usually much smoother than parsing it because Jackson can just read your getter methods. I usually recommend using writerWithDefaultPrettyPrinter() during development so the output isn't one giant, unreadable line.
UserProfile profile = new UserProfile("coder123", "code@java.org");
ObjectMapper mapper = new ObjectMapper();
// Standard compact JSON
String compactJson = mapper.writeValueAsString(profile);
// Readable JSON for logs or debugging
String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(profile);
System.out.println(prettyJson);
Handling Unexpected Fields
One thing that will bite you later is the "Unknown Property" error. Imagine your API evolves and starts sending a "phoneNumber" field, but your UserProfile class doesn't have that field yet. By default, Jackson will throw an exception and crash your whole request.
I always configure my ObjectMapper to be lenient. It's better to ignore data you don't need than to crash the entire application because the server added a new field.
ObjectMapper mapper = new ObjectMapper();
// This is a lifesaver in production environments
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Alternatively, if you only want to ignore unknown properties for one specific class, you can drop @JsonIgnoreProperties(ignoreUnknown = true) at the top of the class definition. I personally prefer the global ObjectMapper configuration because it ensures consistency across your entire project.
📋 Practical Task
Implementing a Game Character Save System
You are building a save-game system for an RPG. You need to create a system that can convert a GameCharacter object into a JSON file and load it back later.
Requirements:
- Create a
GameCharacterclass with the following fields:String name,int level, andList<String> inventory. - Ensure the class is compatible with Jackson (think about the constructor issue we discussed).
- Write a
SaveSystemclass with two methods:String exportCharacter(GameCharacter character): Converts the character to a "pretty-printed" JSON string.GameCharacter importCharacter(String json): Parses the JSON string back into aGameCharacterobject.
- The Twist: In your test main method, try to import a JSON string that contains an extra field (e.g.,
"strength": 15). Configure yourObjectMapperso that the program does not crash when it encounters this unknown field.
There are no comments for now.