Skip to content

Remove Jackson/Custom Serialize OpenAPI json #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions http-generator-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

<properties>
<swagger.version>2.0.8</swagger.version>
<jackson.version>2.13.4.1</jackson.version>
</properties>

<dependencies>
Expand All @@ -36,12 +35,6 @@
<version>1.21-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-models</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package io.avaje.http.generator.core.openapi;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
Expand Down Expand Up @@ -177,28 +173,19 @@ public void readApiDefinition(Element element) {
}

public void writeApi() {
try (Writer metaWriter = createMetaWriter()) {

OpenAPI openAPI = getApiForWriting();
ObjectMapper mapper = createObjectMapper();
mapper.writeValue(metaWriter, openAPI);
final var openAPI = getApiForWriting();
try (var metaWriter = createMetaWriter()) {

} catch (IOException e) {
final var json = OpenAPISerializer.serialize(openAPI);
JsonFormatter.prettyPrintJson(metaWriter, json);

} catch (final Exception e) {
logError(null, "Error writing openapi file" + e.getMessage());
e.printStackTrace();
}
}

private ObjectMapper createObjectMapper() {

ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.enable(SerializationFeature.INDENT_OUTPUT);

return mapper;
}

private Writer createMetaWriter() throws IOException {
FileObject writer = filer.createResource(StandardLocation.CLASS_OUTPUT, "meta", "openapi.json");
return writer.openWriter();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.avaje.http.generator.core.openapi;

import java.io.IOException;
import java.io.Writer;

final class JsonFormatter {

private JsonFormatter() {}

public static String prettyPrintJson(Writer writer, String json) throws IOException {
final var jsonChars = json.toCharArray();

var indentLevel = 0;
var inString = false;

for (var i = 0; i < jsonChars.length; i++) {
final var current = jsonChars[i];

if (current == '"' && jsonChars[i - 1] != '\\') {
inString = !inString;
writer.append(current);
} else if (inString) {
writer.append(current);
} else {
switch (current) {
case '{':
case '[':
writer.append(current).append("\n");
indentLevel++;
addIndents(writer, indentLevel);
break;
case '}':
case ']':
writer.append("\n");
indentLevel--;
addIndents(writer, indentLevel);
writer.append(current);
break;
case ',':
writer.append(current).append("\n");
addIndents(writer, indentLevel);
break;
case ':':
writer.append(" :");
break;
default:
writer.append(current);
break;
}
}
}

return writer.toString();
}

private static void addIndents(Writer writer, int level) throws IOException {
for (var i = 0; i < level; i++) {
writer.append("\t");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package io.avaje.http.generator.core.openapi;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

final class OpenAPISerializer {

private OpenAPISerializer() {}

/**
* Converts the given object into a serialized string.
*
* @param obj the object to serialize
* @return the serialized string
* @throws IllegalAccessException if the fields of the object cannot be accessed
*/
static String serialize(Object obj) throws IllegalAccessException {

final Class<?> cls = obj.getClass();

final var sb = new StringBuilder();
var firstElement = true;
// handle collections and maps differently to avoid module errors
if (obj instanceof Collection) {
sb.append("[");
final var collection = (Collection) obj;
for (final Object element : collection) {

if (!firstElement) {
sb.append(",");
}

write(sb, element);
firstElement = false;
}

sb.append("]");

} else {

if (obj instanceof Map) {

sb.append("{");
final Map<?, ?> map = (Map<?, ?>) obj;
for (final Map.Entry<?, ?> entry : map.entrySet()) {

if (!firstElement) {
sb.append(",");
}
sb.append("\"");
sb.append(entry.getKey());
sb.append("\": ");

write(sb, entry.getValue());
firstElement = false;
}
} else {

sb.append("{");
if (obj instanceof String) {
System.out.println();
}

final var fields = getAllFields(cls);

var firstField = true;
for (final Field field : fields) {
field.setAccessible(true);
final var value = field.get(obj);
if (value != null) {

if (!firstField) {
sb.append(",");
}
sb.append("\"");
sb.append(field.getName());
sb.append("\": ");
write(sb, value);
firstField = false;
}
}
}
sb.append("}");
}
return sb.toString();
}

/**
* Gets all the fields of the given class and its superclass. Will skip fields of java lang
* classes
*
* @param clazz the class to get the fields for
* @return an array of fields
*/
static Field[] getAllFields(Class<?> clazz) {
final var fields = clazz.getDeclaredFields();
Class<?> superclass = clazz.getSuperclass();
if (superclass.getCanonicalName().startsWith("java.")) {
superclass = null;
}
if (superclass != null) {
final var superFields = getAllFields(superclass);
final var allFields = new Field[fields.length + superFields.length];
System.arraycopy(fields, 0, allFields, 0, fields.length);
System.arraycopy(superFields, 0, allFields, fields.length, superFields.length);
return allFields;
} else {
return fields;
}
}

static boolean isPrimitiveWrapperType(Object value) {

return value instanceof Boolean
|| value instanceof Character
|| value instanceof Byte
|| value instanceof Short
|| value instanceof Integer
|| value instanceof Long
|| value instanceof Float
|| value instanceof Double;
}

/**
* Extracts the primitive value from the given object if it is a wrapper for a primitive type.
*
* @param object the object to extract the value from
* @return the primitive value if the object is a wrapper, the object itself otherwise
*/
static Object extractPrimitiveValue(Object object) {
if (object instanceof Boolean) {
return (boolean) object;
} else if (object instanceof Character) {
return (char) object;
} else if (object instanceof Byte) {
return (byte) object;
} else if (object instanceof Short) {
return (short) object;
} else if (object instanceof Integer) {
return (int) object;
} else if (object instanceof Long) {
return (long) object;
} else if (object instanceof Float) {
return (float) object;
} else if (object instanceof Double) {
return (double) object;
} else {
return object;
}
}

/**
* Appends the given value to the string builder.
*
* @param sb the string builder to append to
* @param value the value to append
* @throws IllegalAccessException if the fields of the value object cannot be accessed
*/
static void write(StringBuilder sb, Object value) throws IllegalAccessException {
final var isprimitiveWrapper = isPrimitiveWrapperType(value);
// Append primitive or string value as is
if (value.getClass().isPrimitive() || value instanceof String || isprimitiveWrapper) {
if (isprimitiveWrapper) {
sb.append(extractPrimitiveValue(value));
} else {
sb.append("\"");
sb.append(value.toString().replace("\"", "\\\""));
sb.append("\"");
}
} else {
// Recursively handle other object types
sb.append(serialize(value));
}
}
}
2 changes: 0 additions & 2 deletions http-generator-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
requires transitive io.avaje.http.api;
requires transitive io.swagger.v3.oas.models;
requires transitive io.swagger.v3.oas.annotations;
requires transitive com.fasterxml.jackson.core;
requires transitive com.fasterxml.jackson.databind;
requires transitive com.fasterxml.jackson.annotation;
requires transitive java.validation;
}