joelittlejohn / jsonschema2pojo

Generate Java types from JSON or JSON Schema and annotate those types for data-binding with Jackson, Gson, etc
http://www.jsonschema2pojo.org
Apache License 2.0
6.22k stars 1.66k forks source link

how to implement a typed interface using jcodemodel ? #1536

Closed joerg-d-schneider-db closed 1 year ago

joerg-d-schneider-db commented 1 year ago

Hi,

I'm currently implementing a customized rule class to overcome some limitation in handling enums when generating JSONB output.

I'm stuck at the point where I need to create an inner class that implements a typed interface of JsonbAdapter <T, String>

Using JCodeModel statement of

// create an public static inner class inside the enum

            JDefinedClass c = _enum._class(JMod.PUBLIC | JMod.STATIC,
                    enumDefinition.getNodeName() + EnumMapperRule.SUFFIX);

c._implements(JsonbAdapter.class)

just gives me

public static class EventTypeMapper implements JsonbAdapter {

What I actually need is

public static class EventTypeMapper implements JsonbAdapter<EventType, String> {

Any idea how that can be achieved with JCodeModel ?

Any help appreciated

joelittlejohn commented 1 year ago

You need to 'narrow' the class. Try using the .narrow method on the JDefinedClass and pass in the enum type and String type as arguments.

joerg-d-schneider-db commented 1 year ago

Hi,

I tried that and roughly 100+ other approaches - still no luck. Sorry for my ignorance.

My custom rule still produces "public static class EventTypeMapper implements JsonbAdapter {" instead of " public static class EventTypeMapper implements JsonbAdapter<EventType, String> {"

Here is the entire code base I'm stuck with

package com.db.dice.rules;

import java.util.List;

import javax.json.bind.adapter.JsonbAdapter;

import org.jsonschema2pojo.AnnotationStyle; import org.jsonschema2pojo.model.EnumDefinition; import org.jsonschema2pojo.rules.EnumRule; import org.jsonschema2pojo.rules.RuleFactory;

import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod;

public class EnumMapperRule extends EnumRule {

private final RuleFactory ruleFactory;
public static final String SUFFIX = "Mapper";

protected EnumMapperRule(RuleFactory ruleFactory) {
    super(ruleFactory);
    this.ruleFactory = ruleFactory;
}

@Override
protected void applyCustomizations(EnumDefinition enumDefinition, JDefinedClass _enum) {
    if (this.ruleFactory.getGenerationConfig().getAnnotationStyle().equals(AnnotationStyle.JSONB1)
            || this.ruleFactory.getGenerationConfig().getAnnotationStyle().equals(AnnotationStyle.JSONB1)) {
        try {
            // create an public static inner class inside the enum
            JDefinedClass c = _enum._class(JMod.PUBLIC | JMod.STATIC,
                    enumDefinition.getNodeName() + EnumMapperRule.SUFFIX);

            if (c != null) {
                // let the class implement JsonbAdapter interface - to do : how to handle generics !!!
                JClass temp1 = _enum.owner().ref(JsonbAdapter.class);
                if(temp1.isInterface()) {
                    System.out.println("JsonbAdapter is Interface");
                    temp1.generify("String", String.class);
                    temp1.narrow(String.class);

                } else {
                    System.out.println("JsonbAdapter is not an Interface");                 
                }

                JDefinedClass temp;
                try {
                    temp = _enum.owner()._class("javax.json.bind.adapter.JsonbAdapter");
                } catch(JClassAlreadyExistsException ae) {
                    temp = _enum.owner()._getClass("javax.json.bind.adapter.JsonbAdapter");                     
                }

                if(temp != null) {
                    temp.generify("String", String.class);
                    temp.narrow(Object.class);
                } else {
                    System.out.println("temp class is null !!!");
                }

                JDefinedClass adapter = c._implements(temp1);
                adapter.narrow(String.class);
                // create the adoptToJson method
                JMethod to = c.method(JMod.PUBLIC, String.class, "adaptToJson");
                to.annotate(Override.class);
                to.param(_enum, "obj");
                to.body().directStatement("return obj.value();");

                // create the adoptFromJson method
                JMethod from = c.method(JMod.PUBLIC, _enum, "adaptFromJson");
                from.annotate(Override.class);
                from.param(String.class, "obj");
                from.body().directStatement("return " + enumDefinition.getNodeName() + ".fromValue(obj);");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

}

joerg-d-schneider-db commented 1 year ago

finally made it

        c._implements(model.ref(JsonbAdapter.class).narrow(Object.class).narrow(String.class));

produces

public class TestClass implements JsonbAdapter<Object, String>

Thx for your help - that was the last piece missing ;-)