Skip to content

Commit 263e0dc

Browse files
authored
Merge pull request #120 from SentryMan/remove-jackson
Remove Jackson/Custom Serialize OpenAPI json
2 parents 664e93b + a67b1c8 commit 263e0dc

File tree

5 files changed

+243
-28
lines changed

5 files changed

+243
-28
lines changed

http-generator-core/pom.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
<properties>
1515
<swagger.version>2.0.8</swagger.version>
16-
<jackson.version>2.13.4.1</jackson.version>
1716
</properties>
1817

1918
<dependencies>
@@ -36,12 +35,6 @@
3635
<version>1.21-SNAPSHOT</version>
3736
</dependency>
3837

39-
<dependency>
40-
<groupId>com.fasterxml.jackson.core</groupId>
41-
<artifactId>jackson-databind</artifactId>
42-
<version>${jackson.version}</version>
43-
</dependency>
44-
4538
<dependency>
4639
<groupId>io.swagger.core.v3</groupId>
4740
<artifactId>swagger-models</artifactId>

http-generator-core/src/main/java/io/avaje/http/generator/core/openapi/DocContext.java

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package io.avaje.http.generator.core.openapi;
22

3-
import com.fasterxml.jackson.annotation.JsonInclude;
4-
import com.fasterxml.jackson.databind.DeserializationFeature;
5-
import com.fasterxml.jackson.databind.ObjectMapper;
6-
import com.fasterxml.jackson.databind.SerializationFeature;
73
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
84
import io.swagger.v3.oas.annotations.tags.Tag;
95
import io.swagger.v3.oas.annotations.tags.Tags;
@@ -177,28 +173,19 @@ public void readApiDefinition(Element element) {
177173
}
178174

179175
public void writeApi() {
180-
try (Writer metaWriter = createMetaWriter()) {
181176

182-
OpenAPI openAPI = getApiForWriting();
183-
ObjectMapper mapper = createObjectMapper();
184-
mapper.writeValue(metaWriter, openAPI);
177+
final var openAPI = getApiForWriting();
178+
try (var metaWriter = createMetaWriter()) {
185179

186-
} catch (IOException e) {
180+
final var json = OpenAPISerializer.serialize(openAPI);
181+
JsonFormatter.prettyPrintJson(metaWriter, json);
182+
183+
} catch (final Exception e) {
187184
logError(null, "Error writing openapi file" + e.getMessage());
188185
e.printStackTrace();
189186
}
190187
}
191188

192-
private ObjectMapper createObjectMapper() {
193-
194-
ObjectMapper mapper = new ObjectMapper();
195-
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL)
196-
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
197-
.enable(SerializationFeature.INDENT_OUTPUT);
198-
199-
return mapper;
200-
}
201-
202189
private Writer createMetaWriter() throws IOException {
203190
FileObject writer = filer.createResource(StandardLocation.CLASS_OUTPUT, "meta", "openapi.json");
204191
return writer.openWriter();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.avaje.http.generator.core.openapi;
2+
3+
import java.io.IOException;
4+
import java.io.Writer;
5+
6+
final class JsonFormatter {
7+
8+
private JsonFormatter() {}
9+
10+
public static String prettyPrintJson(Writer writer, String json) throws IOException {
11+
final var jsonChars = json.toCharArray();
12+
13+
var indentLevel = 0;
14+
var inString = false;
15+
16+
for (var i = 0; i < jsonChars.length; i++) {
17+
final var current = jsonChars[i];
18+
19+
if (current == '"' && jsonChars[i - 1] != '\\') {
20+
inString = !inString;
21+
writer.append(current);
22+
} else if (inString) {
23+
writer.append(current);
24+
} else {
25+
switch (current) {
26+
case '{':
27+
case '[':
28+
writer.append(current).append("\n");
29+
indentLevel++;
30+
addIndents(writer, indentLevel);
31+
break;
32+
case '}':
33+
case ']':
34+
writer.append("\n");
35+
indentLevel--;
36+
addIndents(writer, indentLevel);
37+
writer.append(current);
38+
break;
39+
case ',':
40+
writer.append(current).append("\n");
41+
addIndents(writer, indentLevel);
42+
break;
43+
case ':':
44+
writer.append(" :");
45+
break;
46+
default:
47+
writer.append(current);
48+
break;
49+
}
50+
}
51+
}
52+
53+
return writer.toString();
54+
}
55+
56+
private static void addIndents(Writer writer, int level) throws IOException {
57+
for (var i = 0; i < level; i++) {
58+
writer.append("\t");
59+
}
60+
}
61+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package io.avaje.http.generator.core.openapi;
2+
3+
import java.lang.reflect.Field;
4+
import java.util.Collection;
5+
import java.util.Map;
6+
7+
final class OpenAPISerializer {
8+
9+
private OpenAPISerializer() {}
10+
11+
/**
12+
* Converts the given object into a serialized string.
13+
*
14+
* @param obj the object to serialize
15+
* @return the serialized string
16+
* @throws IllegalAccessException if the fields of the object cannot be accessed
17+
*/
18+
static String serialize(Object obj) throws IllegalAccessException {
19+
20+
final Class<?> cls = obj.getClass();
21+
22+
final var sb = new StringBuilder();
23+
var firstElement = true;
24+
// handle collections and maps differently to avoid module errors
25+
if (obj instanceof Collection) {
26+
sb.append("[");
27+
final var collection = (Collection) obj;
28+
for (final Object element : collection) {
29+
30+
if (!firstElement) {
31+
sb.append(",");
32+
}
33+
34+
write(sb, element);
35+
firstElement = false;
36+
}
37+
38+
sb.append("]");
39+
40+
} else {
41+
42+
if (obj instanceof Map) {
43+
44+
sb.append("{");
45+
final Map<?, ?> map = (Map<?, ?>) obj;
46+
for (final Map.Entry<?, ?> entry : map.entrySet()) {
47+
48+
if (!firstElement) {
49+
sb.append(",");
50+
}
51+
sb.append("\"");
52+
sb.append(entry.getKey());
53+
sb.append("\": ");
54+
55+
write(sb, entry.getValue());
56+
firstElement = false;
57+
}
58+
} else {
59+
60+
sb.append("{");
61+
if (obj instanceof String) {
62+
System.out.println();
63+
}
64+
65+
final var fields = getAllFields(cls);
66+
67+
var firstField = true;
68+
for (final Field field : fields) {
69+
field.setAccessible(true);
70+
final var value = field.get(obj);
71+
if (value != null) {
72+
73+
if (!firstField) {
74+
sb.append(",");
75+
}
76+
sb.append("\"");
77+
sb.append(field.getName());
78+
sb.append("\": ");
79+
write(sb, value);
80+
firstField = false;
81+
}
82+
}
83+
}
84+
sb.append("}");
85+
}
86+
return sb.toString();
87+
}
88+
89+
/**
90+
* Gets all the fields of the given class and its superclass. Will skip fields of java lang
91+
* classes
92+
*
93+
* @param clazz the class to get the fields for
94+
* @return an array of fields
95+
*/
96+
static Field[] getAllFields(Class<?> clazz) {
97+
final var fields = clazz.getDeclaredFields();
98+
Class<?> superclass = clazz.getSuperclass();
99+
if (superclass.getCanonicalName().startsWith("java.")) {
100+
superclass = null;
101+
}
102+
if (superclass != null) {
103+
final var superFields = getAllFields(superclass);
104+
final var allFields = new Field[fields.length + superFields.length];
105+
System.arraycopy(fields, 0, allFields, 0, fields.length);
106+
System.arraycopy(superFields, 0, allFields, fields.length, superFields.length);
107+
return allFields;
108+
} else {
109+
return fields;
110+
}
111+
}
112+
113+
static boolean isPrimitiveWrapperType(Object value) {
114+
115+
return value instanceof Boolean
116+
|| value instanceof Character
117+
|| value instanceof Byte
118+
|| value instanceof Short
119+
|| value instanceof Integer
120+
|| value instanceof Long
121+
|| value instanceof Float
122+
|| value instanceof Double;
123+
}
124+
125+
/**
126+
* Extracts the primitive value from the given object if it is a wrapper for a primitive type.
127+
*
128+
* @param object the object to extract the value from
129+
* @return the primitive value if the object is a wrapper, the object itself otherwise
130+
*/
131+
static Object extractPrimitiveValue(Object object) {
132+
if (object instanceof Boolean) {
133+
return (boolean) object;
134+
} else if (object instanceof Character) {
135+
return (char) object;
136+
} else if (object instanceof Byte) {
137+
return (byte) object;
138+
} else if (object instanceof Short) {
139+
return (short) object;
140+
} else if (object instanceof Integer) {
141+
return (int) object;
142+
} else if (object instanceof Long) {
143+
return (long) object;
144+
} else if (object instanceof Float) {
145+
return (float) object;
146+
} else if (object instanceof Double) {
147+
return (double) object;
148+
} else {
149+
return object;
150+
}
151+
}
152+
153+
/**
154+
* Appends the given value to the string builder.
155+
*
156+
* @param sb the string builder to append to
157+
* @param value the value to append
158+
* @throws IllegalAccessException if the fields of the value object cannot be accessed
159+
*/
160+
static void write(StringBuilder sb, Object value) throws IllegalAccessException {
161+
final var isprimitiveWrapper = isPrimitiveWrapperType(value);
162+
// Append primitive or string value as is
163+
if (value.getClass().isPrimitive() || value instanceof String || isprimitiveWrapper) {
164+
if (isprimitiveWrapper) {
165+
sb.append(extractPrimitiveValue(value));
166+
} else {
167+
sb.append("\"");
168+
sb.append(value.toString().replace("\"", "\\\""));
169+
sb.append("\"");
170+
}
171+
} else {
172+
// Recursively handle other object types
173+
sb.append(serialize(value));
174+
}
175+
}
176+
}

http-generator-core/src/main/java/module-info.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
requires transitive io.avaje.http.api;
1010
requires transitive io.swagger.v3.oas.models;
1111
requires transitive io.swagger.v3.oas.annotations;
12-
requires transitive com.fasterxml.jackson.core;
13-
requires transitive com.fasterxml.jackson.databind;
1412
requires transitive com.fasterxml.jackson.annotation;
1513
requires transitive java.validation;
1614
}

0 commit comments

Comments
 (0)