Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON to POJO with custom annotations

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?

like image 878
Gaʀʀʏ Avatar asked Jan 23 '18 15:01

Gaʀʀʏ


People also ask

Can POJO have annotations?

You can attach an annotation to a public class field or a public getter or setter method. Every bound class requires at least an @Id annotation to define the object property that holds the object id. A bound POJO class must contain exactly one @Id annotation.

Can we create custom annotation in spring boot?

In this way, we can create different custom annotations for validation purposes. You can find the full source code here. It is easy to create and use custom annotations in Java. Java developers will be relieved of redundant code by using custom annotations.

Can we create custom annotations Java?

Java Custom annotations or Java User-defined annotations are easy to create and use. The @interface element is used to declare an annotation. For example: @interface MyAnnotation{}

What is @interface annotation in Java?

Annotation is defined like a ordinary Java interface, but with an '@' preceding the interface keyword (i.e., @interface ). You can declare methods inside an annotation definition (just like declaring abstract method inside an interface). These methods are called elements instead.


2 Answers

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.

like image 88
Gaʀʀʏ Avatar answered Sep 25 '22 16:09

Gaʀʀʏ


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

like image 30
Ravi Mangalagiri Avatar answered Sep 25 '22 16:09

Ravi Mangalagiri