Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to java class engine, best practices for implementation, and data security standards.
Java's JSON story is dominated by Jackson, and for good reason — it covers every mapping scenario from simple field renaming to polymorphic type hierarchies to streaming parsers for multi-gigabyte files. Converting JSON to Java classes is not just about generating getters and setters; it's about choosing the right Jackson annotations for your API contract, deciding between mutable POJOs and immutable Records, and wiring validation into the Spring Boot request lifecycle so that malformed JSON is rejected before it reaches your business logic.
// Input JSON
{
"employeeId": "EMP_001",
"fullName": "Robert Martin",
"department": "Engineering",
"email": "robert@company.com",
"skills": ["Java", "Spring", "AWS"],
"startDate": "2020-03-15"
}
// Generated Java Class (Lombok + Jackson)
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.Builder;
import java.time.LocalDate;
import java.util.List;
@Data
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee {
@JsonProperty("employeeId")
private String id;
@JsonProperty("fullName")
private String name;
private String department;
private String email;
private List<String> skills;
private LocalDate startDate;
}
@JsonIgnoreProperties(ignoreUnknown = true) makes the class tolerant of additional JSON fields — useful when consuming external APIs that add new fields. Lombok's @Data generates all getters, setters, equals(), hashCode(), and toString(); @Builder adds a builder pattern for construction.
Java Records are the modern alternative to Lombok for immutable data classes — less boilerplate, built into the language:
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;
import java.util.List;
// Java Record — immutable, all-args constructor, equals, hashCode, toString built in
public record Employee(
@JsonProperty("employeeId") String id,
@JsonProperty("fullName") String name,
String department,
String email,
List<String> skills,
Instant createdAt
) {}
// Jackson deserializes JSON into Records the same way as POJOs
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Employee emp = mapper.readValue(jsonString, Employee.class);
Records are the preferred choice for DTOs, API request/response bodies, and value objects that shouldn't change after creation. Use Lombok POJOs when you need mutability, JPA entity annotations, or framework compatibility that requires no-arg constructors.
The most important annotations for controlling JSON mapping behavior:
import com.fasterxml.jackson.annotation.*;
@JsonRootName("employee")
public class Employee {
// Rename JSON key
@JsonProperty("employee_id")
private String id;
// Accept multiple JSON key names for this field
@JsonAlias({"fullName", "full_name", "name"})
private String displayName;
// Exclude from serialization AND deserialization
@JsonIgnore
private String passwordHash;
// Include only in serialization (not deserialization)
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Instant createdAt;
// Include only in deserialization (not serialization)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
// Use custom serializer/deserializer for this field
@JsonSerialize(using = CustomDateSerializer.class)
@JsonDeserialize(using = CustomDateDeserializer.class)
private LocalDate hireDate;
}
// Polymorphic types — discriminate by "type" field
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ClickEvent.class, name = "click"),
@JsonSubTypes.Type(value = KeypressEvent.class, name = "keypress"),
})
abstract class DomEvent { String sessionId; }
class ClickEvent extends DomEvent { int x; int y; }
class KeypressEvent extends DomEvent { String key; boolean ctrl; }
Combine Jackson deserialization with Bean Validation to reject malformed requests at the controller layer:
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
// Request DTO with validation constraints
public record CreateUserRequest(
@NotBlank @Size(min = 3, max = 50)
String username,
@NotBlank @Email
String email,
@NotBlank @Size(min = 8, message = "Password must be at least 8 characters")
String password,
@Pattern(regexp = "^(admin|user|guest)$", message = "Invalid role")
String role
) {}
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@PostMapping
public ResponseEntity<UserResponse> createUser(
@Valid @RequestBody CreateUserRequest request
) {
// request.username(), request.email() — already validated
// Invalid requests are rejected before reaching this method
User user = userService.create(request);
return ResponseEntity.status(201).body(UserResponse.from(user));
}
}
// Global validation error handler
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, List<String>>> handleValidation(
MethodArgumentNotValidException ex
) {
Map<String, List<String>> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.groupingBy(
FieldError::getField,
Collectors.mapping(FieldError::getDefaultMessage, Collectors.toList())
));
return ResponseEntity.badRequest().body(errors);
}
}
// Required: JavaTimeModule for java.time.* support
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new ISO8601DateFormat());
// In Spring Boot, configure globally via application.properties:
// spring.jackson.serialization.write-dates-as-timestamps=false
// spring.jackson.time-zone=UTC
public record Order(
String id,
LocalDate orderDate, // "2024-01-15" — date only
LocalDateTime processedAt, // "2024-01-15T08:30:00" — no timezone
ZonedDateTime shippedAt, // "2024-01-15T08:30:00Z" — with timezone
Instant createdAt // "2024-01-15T08:30:00.000Z" — epoch-based
) {}
Prefer ZonedDateTime or Instant for timestamps that cross timezones (API timestamps, event times). Use LocalDate for calendar dates without time components (birthdays, deadlines) and LocalDateTime for local times only (scheduled tasks within a single timezone).
ObjectMapper bean does this for you.@JsonIgnoreProperties(ignoreUnknown = true) to inbound DTOs: External API responses evolve — a new field breaks deserialization without this annotation. Add it to all classes that receive external JSON.@JsonNaming for global naming conventions: Instead of annotating every field, apply @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) at the class level to map all fields automatically.Q: Jackson vs. Gson — which should I use?
A: Jackson for Spring Boot projects — it's the default, deeply integrated, and more feature-rich. Gson for Android development or projects where Jackson's size or complexity is prohibitive. Both handle JSON correctly; the difference is in the ecosystem and advanced feature set.
Q: How do I map JSON arrays to Java?
A: Use List<T> or T[] as the field type. For top-level arrays: List<Employee> employees = mapper.readValue(json, new TypeReference<List<Employee>>(){}); — the TypeReference preserves generic type information that would otherwise be erased.
Q: How do I handle null vs. missing fields?
A: Jackson treats both identically by default — missing fields get null (for objects) or zero values (for primitives). To distinguish: use @JsonSetter(nulls = Nulls.FAIL) to fail on explicit null, or @JsonInclude(JsonInclude.Include.NON_NULL) to skip null fields in serialization.
Q: How do I parse a large JSON file without loading it all into memory?
A: Use Jackson's streaming API or ObjectMapper.readValues() with a JsonParser: MappingIterator<Employee> iter = mapper.readerFor(Employee.class).readValues(file). This processes one object at a time, keeping memory usage constant regardless of file size.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.