question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Generator generated Empty Java class from perfectly valid Jackson Schema

See original GitHub issue

Here is the schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://domain/schema/property.schema.json",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "address",
    "propertyValuation",
    "propertyType",
    "bedrooms",
    "keyWorkerScheme",
    "buyToLet",
    "tenure",
    "floors",
    "country",
    "assetCharges"
  ],
  "properties": {
    "address": {
      "$ref": "https://domain/schema/address.schema.json"
    },
    "propertyValuation": {
      "type": "number"
    },
    "propertyType": {
      "type": "string",
      "enum": [
        "House",
        "Terraced Property",
        "Semi-Detached Property",
        "Detached Property",
        "Flat/Maisonette",
        "End Terrace House",
        "Mid Terrace House",
        "Semi-Detached House",
        "Detached House",
        "Semi-Detached Bungalow",
        "Detached Bungalow",
        "Converted Flat",
        "Purpose Built Flat",
        "Retirement Flat (Not Used)",
        "Bungalow Property",
        "Terraced Bungalow",
        "Town House (Not Used)",
        "End Terrace Bungalow",
        "Mid Terrace Bungalow",
        "End Terrace Property",
        "Mid Terrace Property",
        "Terraced House"
      ]
    },
    "bedrooms": {
      "type": "number"
    },
    "keyWorkerScheme": {
      "type": "boolean"
    },
    "buyToLet": {
      "type": "boolean"
    },
    "tenure": {
      "type": "string",
      "enum": ["Leasehold", "Freehold"]
    },
    "floors": {
      "type": "number"
    },
    "country": {
      "type": "string",
      "enum": ["England", "Scotland", "Wales", "Northern Ireland"]
    },
    "assetCharges": {
      "type": "array",
      "items": {
        "$ref": "https://domain/schema/property-asset-charge.json"
      }
    }
  },
  "if": {
    "properties": { "buyToLet": { "const": true } }
  },
  "then": {
    "properties": {
      "buyToLetType": {
        "type": "string",
        "enum": ["Commercial", "Consumer"]
      }
    },
    "required": ["buyToLetType"],
    "if": {
      "properties": { "buyToLetType" : { "const": "Commercial" } }
    },
    "then": {
      "properties": {
        "selfFunding": {
          "type": "boolean"
        }
      },
      "required": ["selfFunding"]
    }
  }
}

Here’s the Java file image:

package domain.model;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Property {


}

Here’s my config:

package domain.util;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import com.sun.codemodel.*;
import org.jsonschema2pojo.*;
import org.jsonschema2pojo.rules.RuleFactory;
import javax.validation.constraints.NotNull;


public class GenerateClassFromSchema
{
  private static final String BASE_PATH = "src";
  private static final SchemaMapper schemaMapper;
  private static final JCodeModel schemaTransformer;


  static {
    GenerationConfig config = new DefaultGenerationConfig() {
      @Override
      public boolean isIncludeGeneratedAnnotation() {
        return false;
      }
    };
    schemaMapper = new SchemaMapper(new RuleFactory(
            config, (new Jackson2Annotator(config)),
            (new SchemaStore())
    ), (new SchemaGenerator()));
    schemaTransformer = new JCodeModel();
  }


  private static void addJSONAnnotations(JDefinedClass serializableClass) {
    serializableClass.annotate(com.fasterxml.jackson.annotation.JsonIgnoreProperties.class).
      param("ignoreUnknown",true);
  }
  private static void removeAllSetters(JDefinedClass serializableClass) {
    serializableClass.methods().removeIf((m) ->
      (m.type().name().equals("void") && (!m.params().isEmpty()))
    );
  }
  private static void addJSONCreatorConstructor(JDefinedClass serializableClass) {
    JMethod schemaClassConstructor = serializableClass.constructor(JMod.PUBLIC);
    schemaClassConstructor.annotate(com.fasterxml.jackson.annotation.JsonCreator.class);
    Predicate<String> isRequired = Pattern.compile("required", Pattern.CASE_INSENSITIVE).asPredicate();
    serializableClass.fields().forEach((k, v) -> {
      //@TODO: Assumption that the generator will comment all required fields with "..(Required).."
      String paramName = ('_'+k);
      if (isRequired.test(v.javadoc().toString())) {
        schemaClassConstructor.param(v.type(), paramName).
          annotate(com.fasterxml.jackson.annotation.JsonProperty.class).
          param("value", k).
          param("required", true);
      } else
        schemaClassConstructor.param(v.type(), paramName).
          annotate(com.fasterxml.jackson.annotation.JsonProperty.class).
          param("value", k);
      schemaClassConstructor.body().assign(v, schemaClassConstructor.params().get(schemaClassConstructor.params().size() - 1));
    });
  }

  public static List<String> classFromSchema(@NotNull() String namespace, @NotNull() String className, @NotNull() String schema) {
    JType thePackage = null;
    try {
      thePackage = schemaMapper.generate(schemaTransformer, className, namespace, schema);
    } catch (IOException e) {
      System.err.println(e.getMessage());
    }
    ArrayList<String> classLocations = new ArrayList<String>();
    // 0. Get generated class to modify:
    JClass boxedPackage = thePackage.boxify();
    if (!Objects.isNull(boxedPackage))
      boxedPackage._package().classes().forEachRemaining((c) -> {
        if (c.name().equals(className)) {
          addJSONAnnotations(c);
          addJSONCreatorConstructor(c);
          removeAllSetters(c);
          //Write the class to file using schemaTransformer:
          File schemaFile = new File(BASE_PATH);
          System.out.println("*Registering model*: " + namespace + '.' + className);
          try {
            schemaTransformer.build(schemaFile);
          } catch (IOException e) {
            System.err.println(e.getMessage());
          }
          classLocations.add(
            schemaFile.getAbsolutePath()+'\\'+namespace.replaceAll("\\.", "\\\\")+
            className+".java"
          );
        }
      });
    else
      System.out.println("Could not register model: "+namespace+'.'+className);
    return classLocations;
  }
}

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:27 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
unkishcommented, Apr 18, 2022

ObjectRule::apply should be called once per object definition.

1reaction
unkishcommented, Apr 14, 2022
    static class CustomObjectRule extends ObjectRule {

        public CustomObjectRule(
                RuleFactory ruleFactory,
                ParcelableHelper parcelableHelper,
                ReflectionHelper reflectionHelper) {
            super(ruleFactory, parcelableHelper, reflectionHelper);
        }

        @Override
        public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _package, Schema schema) {
            final JType result = super.apply(nodeName, node, parent, _package, schema);
            // custom logic goes here
            return result;
        }
    }

    static {
        GenerationConfig config = new DefaultGenerationConfig() {
            @Override
            public boolean isIncludeGeneratedAnnotation() {
                return false;
            }
        };
        schemaMapper = new SchemaMapper(
                new RuleFactory(
                        config,
                        new Jackson2Annotator(config),
                        new SchemaStore()) {

                    @Override
                    public Rule<JPackage, JType> getObjectRule() {
                        return new CustomObjectRule(this, new ParcelableHelper(), getReflectionHelper());
                    }
                },
                new SchemaGenerator());
        schemaTransformer = new JCodeModel();
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Generate JSON schema from Java class - Wilddiary.com
This tutorial shows you how to generate JSON schema from Java class. We will use an open source library called JJSchema to do...
Read more >
Conditional subschemas · Issue #1184 - GitHub
If I try to generate POJOs from the examples from the following site ... Generator generated Empty Java class from perfectly valid Jackson...
Read more >
Generate Java class from JSON? - Stack Overflow
A JsonToJava source class file generator that deduces the schema based on supplied sample json data and generates the necessary java data structures....
Read more >
jsonschema2pojo
Generate Plain Old Java Objects from JSON or JSON-Schema.
Read more >
Generating Java from JSON Typedef schemas
Using generated Java code · Parse the input into a Jackson JsonNode , rather than the generated type. · Validate that the parsed...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found