What this tool does
The converter reads the whole document and builds one type per object shape. All the objects in an array are merged into one type; a key missing from some of them, or null in some of them, gets a boxed type so the gap shows up as null. Fields that are always present use primitives: long for whole numbers, double for numbers written with a decimal point or exponent (and for fields that mix the two), and boolean. Integers beyond the range of long become java.math.BigInteger. Arrays become List<T>, values whose kind varies (a string here, a number there) become Object, and a field that was only ever null or an empty array becomes Object or List<Object> with a comment explaining why. Nested types are declared inside the root type, as nested records or public static classes, so the whole result compiles as one file named after the root type; identical shapes share one type, named after the key it appears under. Keys become camelCase Java names. Java keywords get a Value suffix (class becomes classValue), and so do the few names a record component may not use, such as hashCode and toString. In the class style, a getter Jackson would read under a different name, such as getXCoord() (seen as xcoord) or isActive() (seen as active), gets its own @JsonProperty so each key is read and written exactly once. When the JSON is an array, the root type is the item type and a comment shows the TypeReference to read the list with.
Example input and output
{"order_id": "A-1042", "total": 42.5, "paid": true, "customer": {"id": 7, "email": "ada@example.com"}, "tags": ["express"]}import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public record Order(
@JsonProperty("order_id") String orderId,
double total,
boolean paid,
Customer customer,
List<String> tags
) {
public record Customer(
long id,
String email
) {}
}Read it with new ObjectMapper().readValue(json, Order.class). Only order_id needs @JsonProperty; the other keys already match their Java names.
{"user_id": 7, "active": true}import com.fasterxml.jackson.annotation.JsonProperty;
public class Account {
@JsonProperty("user_id")
private long userId;
private boolean active;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}Classes suit Java 8 to 15 and frameworks that need a no-argument constructor and setters, such as JPA entities or older Spring code.
[{"id": 1, "score": 9.5, "rank": 3}, {"id": 2, "score": null}]// The JSON is an array: read it with
// mapper.readValue(json, new TypeReference<List<Result>>() {})
public record Result(
long id,
Double score,
Long rank
) {}score is null in one item and rank is missing from one, so both are boxed: a primitive would turn them into 0. id is in every item, so it stays a long.
Common errors and fixes
- UnrecognizedPropertyException: Unrecognized field "x" (class Order), not marked as ignorable
- Jackson 2 fails on any key the class does not have, and this response has one your sample did not. Turn on Ignore unknown to add @JsonIgnoreProperties(ignoreUnknown = true), or configure the mapper with DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES set to false. Jackson 3 ignores unknown keys by default.
- A 1.5 in the JSON arrives as 1, or a null as 0, with no error
- Jackson 2 truncates a fraction read into long and, by default, turns null or a missing value into 0 for primitives. The generated types box every field your sample showed as optional or null, but if real data differs, change long to Double or Long, or enable DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES. Jackson 3 rejects the null case: Cannot map `null` into type `long`.
- InvalidDefinitionException: Cannot construct instance of `Order` (no Creators, like default constructor, exist)
- Your Jackson is older than 2.12, which added support for records. Upgrade jackson-databind, or switch the output to Class. With classes, the error usually means a nested class lost its static modifier or a constructor with arguments was added without a no-argument one.
- MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
- This field held a string in your sample but an object in the real data (or the other way round). Convert again with a sample that shows both forms; the field then becomes Object, which Jackson fills with a Map or a String.
- The import com.fasterxml.jackson cannot be resolved
- Add jackson-databind (it brings jackson-annotations) to your build, for example com.fasterxml.jackson.core:jackson-databind for Jackson 2 or tools.jackson.core:jackson-databind for Jackson 3. Both read the same com.fasterxml.jackson.annotation annotations, so the generated code works with either.
Common use cases
- Writing DTOs for a Spring Boot REST client or controller
- Mapping third-party API responses with Jackson's ObjectMapper
- Creating Java 16+ records for immutable message and event payloads
- Modelling Kafka or webhook payloads from captured JSON
- Typing test fixtures for integration tests from real responses
Privacy and security notes
- The JSON you paste is converted to Java by JavaScript in this browser tab; it is not uploaded to DevPrix or kept on a server.
- Values are only used to infer types: the generated records and classes contain key names, never data from the sample.
- Share copies a link that carries the JSON after the # in the URL. Browsers do not send that part to servers, but anyone you give the link to can read the input.
Frequently asked questions
Records or classes?
Records (Java 16+) are short, immutable and give you equals, hashCode and toString for free; Jackson 2.12+ reads them through their constructor. Classes with getters and setters are for Java 8 to 15, frameworks that need a no-argument constructor and setters (JPA entities, some older libraries), or objects you want to change after reading them.
Why are some fields Long or Double instead of long or double?
Primitives cannot be null, so a missing or null value would quietly become 0 or false. Fields that were missing from some objects, or null in some, are boxed so you can see the gap. Lists always hold boxed types, as Java generics require.
Does it work with Gson or Moshi?
The types do, but the annotations do not: Gson and Moshi ignore @JsonProperty. Replace it with Gson's @SerializedName("key") or Moshi's @Json(name = "key"), or rename the fields to match the keys. Gson reads records from version 2.10.
Where do the nested types go?
Inside the root type, as nested records or public static classes, so the output compiles as one file, Root.java (or whatever you name the root type). Refer to them as Order.Customer, or move each one into its own file if your style guide prefers that; Jackson handles both.
How should I type dates and money?
Dates stay String and prices stay double, because JSON has neither type. For dates, change the field to a java.time type such as Instant or LocalDate; Jackson 2 needs the jackson-datatype-jsr310 module registered for that. For money, use BigDecimal, which Jackson reads without rounding.