Skip to content

Commit

Permalink
Serialization (JSON-B vs Jackson) for Helidon SE Server generator (Op…
Browse files Browse the repository at this point in the history
…enAPITools#27)

Serialization (JSON-B vs Jackson) for Helidon  SE Server generator

Signed-off-by: aserkes <[email protected]>
  • Loading branch information
aserkes authored Aug 5, 2022
1 parent 9375263 commit 16683c8
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.meta.features.DocumentationFeature;
Expand Down Expand Up @@ -138,6 +139,9 @@ public void processOpts() {
importMapping.put("UncheckedIOException", "java.io.UncheckedIOException");
importMapping.put("IOException", "java.io.IOException");
importMapping.put("ByteArrayInputStream", "java.io.ByteArrayInputStream");
importMapping.put("ObjectMapper", "com.fasterxml.jackson.databind.ObjectMapper");
importMapping.put("Jsonb", "javax.json.bind.Jsonb");
importMapping.put("JsonbBuilder", "javax.json.bind.JsonbBuilder");

if (!additionalProperties.containsKey(MICROPROFILE_ROOT_PACKAGE_PROPERTY)) {
additionalProperties.put(MICROPROFILE_ROOT_PACKAGE_PROPERTY, MICROPROFILE_REST_CLIENT_DEFAULT_ROOT_PACKAGE);
Expand Down Expand Up @@ -195,6 +199,11 @@ public void processOpts() {
additionalProperties.put(SERIALIZATION_LIBRARY_JACKSON, "true");
additionalProperties.remove(SERIALIZATION_LIBRARY_JSONB);
supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java"));
if (isLibrary(HELIDON_SE)) {
supportingFiles.add(new SupportingFile("jsonProvider.mustache",
(sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator),
"JsonProvider.java"));
}
break;
case SERIALIZATION_LIBRARY_JSONB:
additionalProperties.put(SERIALIZATION_LIBRARY_JSONB, "true");
Expand All @@ -212,6 +221,13 @@ public void processOpts() {
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List<Server> servers) {
CodegenOperation codegenOperation = super.fromOperation(path, httpMethod, operation, servers);
if (HELIDON_SE.equals(getLibrary())) {
if (additionalProperties.containsKey(JACKSON)){
codegenOperation.imports.add("ObjectMapper");
}
if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JSONB)){
codegenOperation.imports.add("Jsonb");
codegenOperation.imports.add("JsonbBuilder");
}
if (codegenOperation.bodyParam != null) {
codegenOperation.imports.add("Handler");
}
Expand Down Expand Up @@ -279,6 +295,19 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
return objs;
}

@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
super.postProcessModelProperty(model, property);

if (Boolean.TRUE.equals(model.hasEnums)) {
// Add imports for Jackson
if (additionalProperties.containsKey(JACKSON)) {
model.imports.add("JsonValue");
model.imports.add("JsonCreator");
}
}
}

@Override
public CodegenType getTag() {
return CodegenType.SERVER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import io.helidon.webserver.Service;
public abstract class {{classname}} implements Service {
protected static final Logger LOGGER = Logger.getLogger({{classname}}.class.getName());
{{#jackson}}
private static final ObjectMapper MAPPER = JsonProvider.objectMapper();{{/jackson}}
{{#jsonb}}
private static final Jsonb JSONB = JsonbBuilder.create();{{/jsonb}}
protected final Config config;

public {{classname}}(Config config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@ dependencies {
implementation "javax.validation:validation-api:${project.validationApiVersion}"
implementation 'io.helidon.webserver:helidon-webserver'
implementation 'io.helidon.media:helidon-media-jsonp'
{{#jackson}}
implementation 'io.helidon.media:helidon-media-jackson'
{{/jackson}}
{{#jsonb}}
implementation 'io.helidon.media:helidon-media-jsonb'
{{/jsonb}}
implementation 'io.helidon.media:helidon-media-multipart'
implementation 'io.helidon.config:helidon-config-yaml'
implementation 'io.helidon.health:helidon-health'
implementation 'io.helidon.health:helidon-health-checks'
implementation 'io.helidon.metrics:helidon-metrics'
implementation 'io.helidon.openapi:helidon-openapi'
implementation 'org.glassfish.jersey.media:jersey-media-json-jackson'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'io.helidon.webclient:helidon-webclient'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,62 @@

public enum {{{datatypeWithEnum}}} {
/**
* {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}}
*/
{{#jsonb}}
@JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class)
@JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class)
{{/jsonb}}
{{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}} {
{{#allowableValues}}{{#enumVars}}{{{name}}}({{{value}}}){{^-last}},
{{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}}

private String value;
private {{{dataType}}} value;

{{{datatypeWithEnum}}}(String value) {
{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{dataType}}} value) {
this.value = value;
}

{{#jackson}}
@JsonValue
{{/jackson}}
public {{{dataType}}} getValue() {
return value;
}

@Override
public String toString() {
return value;
return String.valueOf(value);
}

{{#jsonb}}
public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> {
@Override
public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
if (String.valueOf(b.value).equals(parser.getString())) {
return b;
}
}
{{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}}
}
}

public static final class Serializer implements JsonbSerializer<{{datatypeWithEnum}}> {
@Override
public void serialize({{datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) {
generator.write(obj.value);
}
}
{{/jsonb}}

{{#jackson}}
@JsonCreator
{{/jackson}}
public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue(String text) {
for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
{{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + text + "'");{{/useNullForUnknownEnumValue}}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
{{#jsonb}}import java.lang.reflect.Type;
import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeDeserializer;
import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeSerializer;
import {{rootJavaEEPackage}}.json.bind.serializer.DeserializationContext;
import {{rootJavaEEPackage}}.json.bind.serializer.JsonbDeserializer;
import {{rootJavaEEPackage}}.json.bind.serializer.JsonbSerializer;
import {{rootJavaEEPackage}}.json.bind.serializer.SerializationContext;
import {{rootJavaEEPackage}}.json.stream.JsonGenerator;
import {{rootJavaEEPackage}}.json.stream.JsonParser;
import {{rootJavaEEPackage}}.json.bind.annotation.JsonbProperty;
{{#vendorExtensions.x-has-readonly-properties}}
import {{rootJavaEEPackage}}.json.bind.annotation.JsonbCreator;
{{/vendorExtensions.x-has-readonly-properties}}{{/jsonb}}
{{#jackson}}
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;{{/jackson}}

/**
* {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}}
*/
{{#jsonb}}
@JsonbTypeSerializer({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Serializer.class)
@JsonbTypeDeserializer({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Deserializer.class){{/jsonb}}
public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} {
{{#allowableValues}}
Expand All @@ -15,22 +35,46 @@ public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatyp
this.value = value;
}

{{#jackson}}
@JsonValue
{{/jackson}}
public {{{dataType}}} getValue() {
return value;
}

@Override
public String toString() {
return String.valueOf(value);
}

{{#jsonb}}
public static final class Deserializer implements JsonbDeserializer<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> {
@Override
public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
if (String.valueOf(b.value).equals(parser.getString())) {
return b;
}
}
{{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}}
}
}

public static final class Serializer implements JsonbSerializer<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> {
@Override
public void serialize({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) {
generator.write(obj.value);
}
}
{{/jsonb}}
{{#jackson}}
@JsonCreator{{/jackson}}
public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue(String text) {
for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
{{#isNullable}}
return null;
{{/isNullable}}
{{^isNullable}}
throw new IllegalArgumentException("Unexpected value '" + text + "'");
{{/isNullable}}
{{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + text + "'");{{/useNullForUnknownEnumValue}}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package {{apiPackage}};

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class JsonProvider {
public static ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
return mapper;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import io.helidon.common.reactive.Single;
import io.helidon.config.Config;
import io.helidon.health.HealthSupport;
import io.helidon.health.checks.HealthChecks;
import io.helidon.media.jsonb.JsonbSupport;
import io.helidon.media.jsonp.JsonpSupport;
{{#jsonb}}
import io.helidon.media.jsonb.JsonbSupport;
{{/jsonb}}
{{#jackson}}
import io.helidon.media.jackson.JacksonSupport;
import {{apiPackage}}.JsonProvider;
{{/jackson}}
import io.helidon.metrics.MetricsSupport;
import io.helidon.openapi.OpenAPISupport;
import io.helidon.webserver.Routing;
Expand Down Expand Up @@ -46,7 +52,12 @@ public final class Main {
WebServer server = WebServer.builder(createRouting(config))
.config(config.get("server"))
.addMediaSupport(JsonpSupport.create())
{{#jsonb}}
.addMediaSupport(JsonbSupport.create())
{{/jsonb}}
{{#jackson}}
.addMediaSupport(JacksonSupport.create(JsonProvider.objectMapper()))
{{/jackson}}
.build();

Single<WebServer> webserver = server.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ package {{package}};
{{#imports}}import {{import}};
{{/imports}}
{{#models}}
{{#model}}{{#description}}
/**
* {{{.}}}
*/{{/description}}{{#isEnum}}
{{#model}}{{#isEnum}}
{{>enumOuterClass}}{{/isEnum}}{{^isEnum}}
{{>pojo}}{{/isEnum}}
{{/model}}
Expand Down
Loading

0 comments on commit 16683c8

Please sign in to comment.