10

I'm trying to include Spring Boot annotations in my generated Java code from JSON like these:

@Entity
public class Person {
...
}

and

@Repository
public interface PersonRepository extends CrudRepository<Person, Long> 
{
}

I'm converting from JSON to POJO with this tutorial. What can I add to my json files to make the generated Java classes include annotations @Entity and @Repository? I have yet to find a tutorial or explanation as to how to provide custom annotations.

jsonschema2pojo looks like it could work using a custom annotator when generating classes, but I am wondering if there is anything built-in to Jackson that easily allows custom annotations?

Gaʀʀʏ
  • 4,372
  • 3
  • 39
  • 59
  • The plugin described in your tutorial link converts from JSON Schema to POJO. JSON Schema is not JSON. – DwB Jan 23 '18 at 16:39
  • @DwB jsonschema2pojo allows you to specify the sourceType as JSON or JSONSchema. It doesn't make a difference to me whether it is JSON or JSON Schema as long as I can get generated POJOs with Hibernate annotations. – Gaʀʀʏ Jan 23 '18 at 17:30
  • You can use below online tool to convert json to pojo https://www.workversatile.com/json-to-pojo – Arshad Shaikh Aug 23 '22 at 18:23

3 Answers3

12

jsonschema2pojo's customAnnotator allowed me to add custom annotations to generated java files. The annoyance with it is that your annotator class must be in a separate project and must be included within the plugin. Here's why.

Add the dependency to your pom.xml

<dependency>
    <groupId>org.jsonschema2pojo</groupId>
    <artifactId>jsonschema2pojo-core</artifactId>
    <version>0.4.0</version>
</dependency>

Add the plugin to the pom.xml plugins

<plugin>
    <groupId>org.jsonschema2pojo</groupId>
    <artifactId>jsonschema2pojo-maven-plugin</artifactId>
    <version>0.5.1</version>
    <dependencies>
        <!-- NOTE: Your annotator MUST come from a dependency -->
        <dependency>
            <groupId>ANNOTATOR_GROUP_ID</groupId>
            <artifactId>ANNOTATOR_ARTIFACT</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <scope>compile</scope>
            <version>1.5.2.RELEASE</version>
        </dependency>
       <!-- NOTE: Any annotation used must have its dependency here!!! -->
    </dependencies>
    <configuration>
        <sourceDirectory>${basedir}/src/main/resources/schema</sourceDirectory>
        <targetPackage>com.test.gen</targetPackage>
        <useCommonsLang3>true</useCommonsLang3>
        <customAnnotator>com.fully.qualified.path.YourAnnotator</customAnnotator>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Create your custom annotator class in a separate project.

package com.deere.gtin_k.pdeaas.work_manager.application;

import com.fasterxml.jackson.databind.JsonNode;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JFieldVar;
import org.jsonschema2pojo.AbstractAnnotator;

import javax.persistence.Entity;

public class HibernateAnnotator extends AbstractAnnotator {

    @Override
    public void propertyField(JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) {
        super.propertyField(field, clazz, propertyName, propertyNode);

        // Note: does not have to be the propertyName, could be the field or propertyNode that is verified.
        if (propertyName.equals("entity")) {
            clazz.annotate(Entity.class);
        }
    }
}

Lastly, the json file:

{
  "title": "Person",
  "type": "object",
  "properties": {
    "entity": true,
    "name": {
      "type": "string"
    }
  }
}

And the final result:

package com.test.gen;

import java.util.HashMap;
import java.util.Map;
import javax.persistence.Entity;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;


/**
 * Person
 * <p>
 * 
 * 
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
@Entity
@JsonPropertyOrder({
    "entity",
    "name"
})
public class Person {

    @JsonProperty("entity")
    private Object entity;
    ...
}

I wish there was a simpler way to do this.

Gaʀʀʏ
  • 4,372
  • 3
  • 39
  • 59
3

In my case I needed to add @ValueOfEnum(enumClass = com.mycompany.SampleType) as suggested in https://www.baeldung.com/javax-validations-enums (5th option)

So I tweaked the override method

@Override
public void propertyField(JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) {
    super.propertyField(field, clazz, propertyName, propertyNode);

    if (propertyNode.get("enumClass") != null) {
        try {
            field.annotate(ValueOfEnum.class).param("enumClass",  Class.forName(propertyNode.get("enumClass").asText()));
        } catch (ClassNotFoundException e) {
            System.err.println("'Not able to add @ValueOfEnum(enumClass = " + propertyNode.get("enumClass").asText() + "' annotation to property :" + propertyName + ". Enum not found in the classpath");
        }
    }
}

Also make sure ValueOfEnum, and the actual enum is avaiable to the plugin. add plugin dependencies in the pom.xml

0

Alternative to annotation decoration

I am guessing at the intent behind the question:

  • You have POJO's generated from a json schema
  • You want to use your ORM (JPA) to manage the POJO's as Entities
  • You want you use Spring Data with the JPA entities

Perhaps an alternative to trying to decorate the generated POJO's with the JPA annotations would be to use an orm.xml. This can be used to define entities and integrate with your ORM as an alternative to annotating the POJO's. This can be useful for existing classes where you can't make changes yourself or perhaps in this case where the code is generated.

Using your generated POJO's, add an orm.xml to your ${project.basedir}/src/main/resources/META-INF instead of adding the annotations

Perhaps something like

orm.xml

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
                 version="2.0">

    <entity class="com.test.gen.Person" access="FIELD">
        <table name="person"/>
        <attributes>
            <id name="id">
                <generated-value strategy="AUTO"/>
            </id>
            <basic name="firstname">
                <column name="firstname" length="200"/>
            </basic>
        </attributes>
    </entity>

</entity-mappings>
Arran Bartish
  • 135
  • 1
  • 12