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.24k stars 1.66k forks source link

[Feature request] Allow javaInterfaces to be passed as plugin configuration #1526

Closed antssilva96 closed 1 year ago

antssilva96 commented 1 year ago

We're using this plugin to generate POJO classes from JSON schema files.

For the code that will be handling the generated classes, it would be best if they could implement an interface to ensure that the available methods are present and avoid using reflection.

I know this is already possible by adding the 'javaInterfaces' in the jsonSchema, but in this particular case the schemas are defined by the user and we would like to hide this implementation detail.

For general purpose this should likely be a map of interface -> classes that should implement it, but for our specific use case we just need the same interface to be applied to all generated classes, so it could be a defaultJavaInterfaces or something like that.

I think this would be a simple change if it is something that you want to support. In the EnumRule and ObjectRule where we currently have

if (node.has("javaInterfaces")) {
    addInterfaces(jclass, node.get("javaInterfaces"));
} 
(...)
private void addInterfaces(JDefinedClass jclass, JsonNode javaInterfaces) {
    for (JsonNode i : javaInterfaces) {
        jclass._implements(jclass.owner().ref(i.asText()));
    }
}

it could be changed to something like

List<String> defaultInterfaces = ruleFactory.getGenerationConfig().getDefaultJavaInterfaces();
Set<String> javaInterfaces = new HashSet<>(defaultInterfaces);
if (node.has("javaInterfaces")) {
    List<String> nodeInterfaces = node.get("javaInterfaces").stream().map(JsonNode::asText).collect(Collectors.toList()));
    javaInterfaces.addAll(nodeInterfaces);
} 
addInterfaces(jclass, javaInterfaces);

(...)
private void addInterfaces(JDefinedClass jclass, Set<String> javaInterfaces) {
    for (String javaInterface : javaInterfaces) {
        jclass._implements(jclass.owner().ref(javaInterface));
    }
}
unkish commented 1 year ago

Hi

I'm afraid that given case/problem is too specific to your domain, thus implementing it might be against jsonschema2pojo "philosophy":

With jsonschema2pojo, we cannot attempt to support every possible preferred way of writing Java source code. Our intention is to create a correct approach that maps between JSON and Java and follows Java conventions. It is not the intention of this library to allow endless configurability to get the exact Java source code that you prefer, as that is too big a task and would create a system that is far harder to extend and maintain. See JAXB for a similar approach - the goal is to provide a correct implementation, not to allow generation of whatever source code style you prefer.

\ That being said it should be possible to achieve desired result by passing custom rule factory & custom rules.

antssilva96 commented 1 year ago

Hello! Yeah, that makes sense.

Could you elaborate on the custom rule factory and rules approach? Is there a way to do this without having to re-compile the plugin and using the new custom version ?

unkish commented 1 year ago

I think that we could take https://github.com/itzg/jsonschema2pojo-rules-bettermaps as an example (maven approach).

Basically an "artifact" would have to be created/published. \ It would need to contain a custom rule factory class similar to https://github.com/itzg/jsonschema2pojo-rules-bettermaps/blob/71392cafe1e439ac81c42f4c21dde8fa59b79cc1/src/main/java/me/itzg/jsonschema2pojo/bettermaps/BetterMapsRuleFactory.java#L16 \ You would also have to override/extend rules that are of interest eg. ObjectRule (note that this is just an example):

public class CustomObjectRule extends ObjectRule {

    protected 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 JDefinedClass jClass = (JDefinedClass) super.apply(nodeName, node, parent, _package, schema);
        // add custom interface's
        jClass._implements(resolveType(jClass._package(), "org.jsonschema2pojo.integration.yaml.YamlTypeIT.InterfaceWithGenerics<String,Integer,Boolean>"));
        return jClass;
    }
}

Once custom rule factory artifact is compiled/published add it as a dependency of the plugin and set the customRuleFactory to point at it (see example in https://github.com/itzg/jsonschema2pojo-rules-bettermaps/blob/master/README.md)

antssilva96 commented 1 year ago

Thanks for the detailed answer! We don't want to own/maintain our own version of the plugin for now, but maybe in the future we have to go that way.

I'll close this issue.