Generating Java from JSON Schema

java dev.to

A new browser-based tool on sjf4j.org turns JSON Schema into Java scaffolding directly in the browser.

Paste a schema, tune the generation strategy, and get Java output immediately.

What makes it more interesting than a basic schema-to-POJO converter is that it exposes modeling decisions directly.

Schema-to-code is not only about mapping types. It is also about deciding what should become part of a stable Java API, and what should remain flexible.

Try it here

๐Ÿ‘‰ Tool: https://sjf4j.org/generator

What the tool does

The generator is a browser-based playground for turning JSON Schema into Java scaffolding.

It currently supports:

  • package and class name configuration
  • primitive / number / date-time mapping choices
  • string enum โ†’ Java enum generation
  • nested object โ†’ inner class generation
  • required-field-aware generation
  • validation annotations such as @NotNull
  • JavaDoc generation from schema metadata
  • allOf flattening for object schemas
  • local $ref expansion
  • deterministic import and member ordering

One of the strongest parts of the tool is its customization model:

  • global configuration for overall generation behavior
  • property-by-property overrides in the Parsed Properties panel

So the tool does not force one all-or-nothing rule across the whole schema. A project can start with a global baseline, then adjust selected members one by one.

A simple example

Given a schema like this:

{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Order","description":"Checkout order created by the storefront.","type":"object","required":["id","amount","createdAt","customer","items"],"properties":{"id":{"type":"string","description":"Business order identifier."},"amount":{"type":"number","description":"Total amount in the settlement currency."},"createdAt":{"type":"string","format":"date-time"},"paid":{"type":"boolean"},"customer":{"type":"object","required":["id","email"],"properties":{"id":{"type":"string"},"email":{"type":"string"}}},"items":{"type":"array","items":{"type":"object","required":["sku","quantity"],"properties":{"sku":{"type":"string"},"quantity":{"type":"integer"}}}}}}
Enter fullscreen mode Exit fullscreen mode

With the default JOJO-oriented settings, the generated Java can look like this:

@Getter @Setter
public class Order extends JsonObject {
    /**
     * JSON shape:
     * <pre>
     * {
     *   "id": "string",
     *   "amount": "number",
     *   "createdAt": "string(date-time)",
     *   "paid": "boolean",
     *   "customer": {
     *     "id": "string",
     *     "email": "string"
     *   },
     *   "items": [
     *     {
     *       "sku": "string",
     *       "quantity": "integer"
     *     }
     *   ]
     * }
     * </pre>
     */
    @NotNull private String id;
    @NotNull private double amount;
    @NotNull private OffsetDateTime createdAt;
    private boolean paid;
    @NotNull private Customer customer;
    @NotNull private List<ItemsItem> items;

    private static final JsonPath PATH_CUSTOMER_EMAIL = JsonPath.compile("$.customer.email");

    public String getCustomerEmail() {
        return PATH_CUSTOMER_EMAIL.getString(this);
    }

    public String getItemsSku(int itemsIndex) {
        return getStringByPath("$.items[" + itemsIndex + "].sku");
    }

    @Getter @Setter
    public static class Customer extends JsonObject {
        @NotNull private String id;
        @NotNull private String email;
    }

    @Getter @Setter
    public static class ItemsItem extends JsonObject {
        @NotNull private String sku;
        @NotNull private int quantity;
    }
}
Enter fullscreen mode Exit fullscreen mode

This is closer to a real API shape: nested objects, arrays, required members, generated inner classes, validation annotations, and root-level path accessors.

Why modeling choices matter

The generator is not designed as a โ€œturn every JSON property into a fieldโ€ machine.

In real systems, the harder question is usually:

Which parts of this schema should become a stable Java contract, and which parts should stay flexible?

That is why the tool exposes decisions such as:

  • JOJO vs POJO modeling
  • field vs property generation
  • path accessor generation on the root model
  • per-property type overrides
  • global defaults plus property-by-property adjustments

This matters when the input schema comes from:

  • a third-party API that changes often
  • an event payload with optional extension fields
  • an internal contract that is stable in some places and fluid in others

Why JOJO is interesting here

This generator is part of the broader SJF4J ecosystem, so it can generate models that fit naturally into SJF4Jโ€™s structural APIs.

If an object is modeled as a JOJO, it extends org.sjf4j.JsonObject.

That gives you an interesting hybrid:

  • typed Java members where you want a stable contract
  • dynamic JSON-style access where you still need flexibility

For example, the generator can produce root-level descendant accessors such as:

public String getCustomerEmail() {
    return PATH_CUSTOMER_EMAIL.getString(this);
}

public String getItemsSku(int itemsIndex) {
    return getStringByPath("$.items[" + itemsIndex + "].sku");
}
Enter fullscreen mode Exit fullscreen mode

That is a different direction from classic schema-to-POJO generators. It is useful when the generated model is not just a DTO, but part of a larger JSON-processing workflow.

Global configuration and per-property tuning

Customization happens at two levels.

At the global level, the generator can define the overall baseline for a file:

  • package and class name
  • JOJO or POJO preference
  • field-generation strategy
  • accessor strategy
  • path-accessor strategy
  • integer / number / boolean / enum / date-time mapping
  • object-leaf mapping
  • validation namespace and annotations
  • JavaDoc generation

At the property level, the Parsed Properties panel can refine specific members:

  • switch between field and property generation
  • override the generated Java type
  • enable or disable by-path getter/setter generation for eligible paths

That makes the tool flexible enough for mixed schemas, where most properties follow one rule but a few important members need a different Java contract.

Where this fits in SJF4J

If you have not seen SJF4J before, it is a unified JSON-semantic processing layer for Java.

It provides one structural model across:

  • parsing
  • modeling
  • JSON Path navigation
  • JSON Patch / Merge Patch
  • JSON Schema validation
  • mapping / transformation

In practice, that means you can:

  1. start from a JSON Schema
  2. generate a Java model
  3. parse payloads into that model
  4. navigate it with JSON Path
  5. validate it with JSON Schema
  6. keep dynamic structure where needed

That end-to-end flow is why this generator fits naturally on the SJF4J site instead of being treated as an isolated utility.

A few implementation details worth keeping explicit

Some current baseline behaviors:

  • nested object schemas generate as public static inner classes
  • nested enums are generated inside the owning class
  • imports are deduplicated and sorted
  • object allOf is flattened before rendering
  • circular local $ref chains are rejected
  • unsupported non-local $ref is ignored for now
  • root path accessors are generated only when the root model is a JOJO

In other words, the tool tries to be predictable first, and flexible second โ€” but without hiding the rules.

Another useful detail: the full generation rules are published on the page itself. So the tool is not a black box. The page also works as a spec-like baseline and regression target: the rules are explicit enough to be reused as an AI prompt, allowing an LLM to reproduce a similar generator โ€” or help design a more optimized one on top of the same contract.

Why this is useful

The practical value is simple: the first draft of a Java model should be fast to get, but not locked into the wrong abstraction too early.

If you only need quick scaffolding, the generator already helps.

If you care about long-term maintainability, the more useful part is that it makes the trade-offs visible:

  • strict vs flexible object modeling
  • field-backed vs dynamic property-backed members
  • plain DTO generation vs JSON-semantic access patterns

Try it and tell me what is missing

If you want to try it:

If you are already working with JSON Schema, Java DTO generation, or mixed typed/dynamic payloads, useful feedback would include:

  • nullable handling
  • multi-file output layout
  • more $ref composition cases
  • better defaults for boxed vs primitive types

Schema-to-code is easy to automate.

Designing good Java boundaries is still the hard part.

Source: dev.to

arrow_back Back to Tutorials