JSON to Java Class Converter

Paste a JSON document and get Java classes annotated for Jackson, ready to deserialize with ObjectMapper. The JSON is analysed in your browser, never on a server.

JSON → Java

Input

Settings

History

Load from URL

POJOs without the boilerplate

Mapping JSON in Java usually means a POJO per object: private fields, a getter and setter for each, and @JsonProperty annotations wherever the JSON key differs from the Java name. For a realistic API response that can be hundreds of lines. This generator writes them from a sample payload, in a form Spring Boot, Micronaut, Quarkus and plain Jackson all understand.

What the generated file contains

Everything is merged into one compilation unit so you can paste it as Root.java: a single package line, the combined imports, the root class as the only public class, and every nested class as a package-private class in the same file. A comment at the top shows how to read data: new ObjectMapper().readValue(json, Root.class).

Each class has:

  • private fields with camelCase names (first-name becomes firstName),
  • a getter and setter per field, both annotated with @JsonProperty("first-name") so the original key is used for reading and writing,
  • java.util.List<T> for arrays.

If the JSON’s root is an array, the element class takes the root name and the usage comment shows a TypeReference<List<...>> instead.

Type choices

  • Whole numbers become long, decimals become double, booleans become boolean, and text becomes String.
  • When a numeric or boolean field is missing from some array items, or is null in some, the boxed type is used (Long, Double, Boolean) so the value can be null instead of silently becoming 0 or false.
  • A field that is only ever null is typed Object.
  • Arrays that mix types — for example numbers and strings — get a small wrapper class with a custom Jackson JsonDeserializer and JsonSerializer, holding one field per possible type.
  • Objects found in an array are merged into a single class covering all keys.

Strings written exactly as a calendar date (2024-03-11) are typed java.time.LocalDate, which Jackson reads once the jackson-datatype-jsr310 module is registered. Full timestamps, UUIDs and enum-like values stay String, because one sample cannot prove their format; adjust those fields by hand.

Options and tips

Root type name (default Root) names the top-level class. Package (default com.example) sets the package declaration; it must be a dotted Java identifier such as com.acme.billing, otherwise the default is used.

If your project uses Lombok or Java records, you can collapse the getters and setters afterwards; the field list and annotations are the useful part. Configure DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES to false if the API may add keys later. Integers beyond 2^53 in the sample are flagged; use BigInteger or String for them. Nothing is uploaded while generating, and the Java formatter can restyle the output to your conventions.

Examples

Order with line items

Produces a public Order class plus Customer and Item classes; giftWrap becomes a boxed Boolean because only one item has it.

Input
{
  "orderId": "ord_8f2k1",
  "status": "paid",
  "total": 287.9,
  "customer": { "id": 1042, "name": "Aisha Tan", "vip": true },
  "items": [
    { "sku": "KB-104", "qty": 1, "price": 129.9 },
    { "sku": "MS-220", "qty": 2, "price": 79.0, "giftWrap": true }
  ]
}
Output
package com.acme.shop;

import com.fasterxml.jackson.annotation.*;
import java.util.List;

// Deserialize with Jackson: Order data = new ObjectMapper().readValue(json, Order.class);

public class Order {
    private String orderID;
    private String status;
    private double total;
    private Customer customer;
    private List<Item> items;

    @JsonProperty("orderId")
    public String getOrderID() { return orderID; }
    @JsonProperty("orderId")
    public void setOrderID(String value) { this.orderID = value; }

    @JsonProperty("status")
    public String getStatus() { return status; }
    @JsonProperty("status")
    public void setStatus(String value) { this.status = value; }

    @JsonProperty("total")
    public double getTotal() { return total; }
    @JsonProperty("total")
    public void setTotal(double value) { this.total = value; }

    @JsonProperty("customer")
    public Customer getCustomer() { return customer; }
    @JsonProperty("customer")
    public void setCustomer(Customer value) { this.customer = value; }

    @JsonProperty("items")
    public List<Item> getItems() { return items; }
    @JsonProperty("items")
    public void setItems(List<Item> value) { this.items = value; }
}

class Customer {
    private long id;
    private String name;
    private boolean vip;

    @JsonProperty("id")
    public long getID() { return id; }
    @JsonProperty("id")
    public void setID(long value) { this.id = value; }

    @JsonProperty("name")
    public String getName() { return name; }
    @JsonProperty("name")
    public void setName(String value) { this.name = value; }

    @JsonProperty("vip")
    public boolean getVip() { return vip; }
    @JsonProperty("vip")
    public void setVip(boolean value) { this.vip = value; }
}

class Item {
    private String sku;
    private long qty;
    private double price;
    private Boolean giftWrap;

    @JsonProperty("sku")
    public String getSku() { return sku; }
    @JsonProperty("sku")
    public void setSku(String value) { this.sku = value; }

    @JsonProperty("qty")
    public long getQty() { return qty; }
    @JsonProperty("qty")
    public void setQty(long value) { this.qty = value; }

    @JsonProperty("price")
    public double getPrice() { return price; }
    @JsonProperty("price")
    public void setPrice(double value) { this.price = value; }

    @JsonProperty("giftWrap")
    public Boolean getGiftWrap() { return giftWrap; }
    @JsonProperty("giftWrap")
    public void setGiftWrap(Boolean value) { this.giftWrap = value; }
}
Open this example in the tool

Hyphenated keys and nulls

Each hyphenated key maps to a camelCase field with @JsonProperty keeping the original name, and the always-null last-login is typed Object.

Input
{
  "user-id": 42,
  "display-name": "Ben Okafor",
  "last-login": null,
  "roles": ["editor", "viewer"]
}
Output
package com.example;

import com.fasterxml.jackson.annotation.*;
import java.util.List;

// Deserialize with Jackson: Root data = new ObjectMapper().readValue(json, Root.class);

public class Root {
    private long userID;
    private String displayName;
    private Object lastLogin;
    private List<String> roles;

    @JsonProperty("user-id")
    public long getUserID() { return userID; }
    @JsonProperty("user-id")
    public void setUserID(long value) { this.userID = value; }

    @JsonProperty("display-name")
    public String getDisplayName() { return displayName; }
    @JsonProperty("display-name")
    public void setDisplayName(String value) { this.displayName = value; }

    @JsonProperty("last-login")
    public Object getLastLogin() { return lastLogin; }
    @JsonProperty("last-login")
    public void setLastLogin(Object value) { this.lastLogin = value; }

    @JsonProperty("roles")
    public List<String> getRoles() { return roles; }
    @JsonProperty("roles")
    public void setRoles(List<String> value) { this.roles = value; }
}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
Unexpected content after the end of the JSON value
Explained
Two JSON documents were pasted one after another.Wrap them in an array or convert them one at a time.
NaN is not a valid JSON number
Explained
The sample came from a system that writes NaN or Infinity, which JSON forbids.Replace those values with null or a number before converting.
Some integers are larger than 2^53; they are typed as floating-point numbers. Use a 64-bit or big-integer type if you need exact values.
Explained
A warning: an integer in the sample exceeds the range inference can handle exactly.Use long if the values fit in 64 bits, otherwise BigInteger.

Frequently asked questions

Which JSON library does the code target?

Jackson (com.fasterxml.jackson). The @JsonProperty annotations and the custom serializers for mixed types are Jackson-specific.

Why are all classes in one file?

So you can paste the output directly. Only the root class is public, which Java allows in a single file; split them into separate files if your style requires it.

Why is a field Long instead of long?

The field was missing or null in part of the sample. A boxed type can hold null, which keeps “no value” distinct from zero.

Can I generate records instead of classes?

Not directly. Convert by hand by keeping the field types and @JsonProperty names in the record components.

Related tools