diff --git a/pom.xml b/pom.xml
index 01092c7..9ca91e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.github.umutayb
Utilities
- 1.7.1
+ 1.7.2-SNAPSHOT
jar
Java-Utilities
@@ -190,6 +190,13 @@
3.0.1
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jsonSchema
+ 2.18.3
+
+
@@ -288,7 +295,7 @@
--pinentry-mode
loopback
- false
+ true
diff --git a/src/main/java/api_assured/ServiceGenerator.java b/src/main/java/api_assured/ServiceGenerator.java
index 0feea32..b039ea8 100644
--- a/src/main/java/api_assured/ServiceGenerator.java
+++ b/src/main/java/api_assured/ServiceGenerator.java
@@ -1,5 +1,10 @@
package api_assured;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.json.JsonWriteFeature;
+import com.fasterxml.jackson.databind.SerializationFeature;
import context.ContextStore;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
@@ -199,9 +204,9 @@ public S generate(Class serviceClass) {
@SuppressWarnings("deprecation")
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
+ .addConverterFactory(JacksonConverterFactory.create(mapper))
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
- .addConverterFactory(JacksonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(SimpleXmlConverterFactory.create()) //Deprecated
.addConverterFactory(MoshiConverterFactory.create())
diff --git a/src/main/java/utils/MappingUtilities.java b/src/main/java/utils/MappingUtilities.java
index be26492..09f266f 100644
--- a/src/main/java/utils/MappingUtilities.java
+++ b/src/main/java/utils/MappingUtilities.java
@@ -3,19 +3,32 @@
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
+/**
+ * Utility class for working with JSON mappings and generating JSON schemas.
+ * Provides methods for serializing and deserializing Java objects to and from JSON.
+ * It also includes functionality to generate JSON schema representations.
+ */
public class MappingUtilities {
/**
- * Visibility settings are applied to allow the mapper to access and serialize/deserialize all fields, while ignoring getters, setters, and creators.
- * The FAIL_ON_EMPTY_BEANS serialization feature is disabled, which prevents the mapper from throwing an exception when encountering empty beans (objects without any properties).
+ * Utility class for handling JSON serialization and deserialization.
+ * Configures an {@link ObjectMapper} for serializing and deserializing Java objects to and from JSON.
*/
public static class Json {
+
+ // An ObjectMapper configured for JSON operations
public static ObjectMapper mapper = new ObjectMapper();
static {
+ // Configure ObjectMapper settings
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
@@ -26,11 +39,11 @@ public static class Json {
}
/**
- * Converts the given object into its JSON string representation in a pretty-printed format.
+ * Converts the given object into a JSON string representation with pretty-print formatting.
*
* @param The type of the object to be converted to JSON.
* @param body The object to be converted to JSON.
- * @return The JSON string representation of the given object in a pretty-printed format.
+ * @return The JSON string representation of the given object in pretty-printed format.
*/
public static String getJsonStringFor(T body) {
return mapper.valueToTree(body).toPrettyString();
@@ -42,13 +55,14 @@ public static String getJsonStringFor(T body) {
* @param The type of the object to be converted to JSON.
* @param body The object to be converted to JSON.
* @return The JSON string representation of the given object in a pretty-printed format.
+ * @throws JsonProcessingException If a JSON processing error occurs.
*/
public static String getJsonString(T body) throws JsonProcessingException {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
}
/**
- * Converts a Json string representation of an object into a Java object of the specified type.
+ * Converts a JSON string representation of an object into a Java object of the specified type.
*
* @param The type of the object.
* @param jsonString The JSON string representation of the object.
@@ -59,5 +73,116 @@ public static String getJsonString(T body) throws JsonProcessingException {
public static T fromJsonString(String jsonString, Class model) throws JsonProcessingException {
return MappingUtilities.Json.mapper.readValue(jsonString, model);
}
+
+ /**
+ * Utility class for generating JSON schema representations.
+ */
+ public static class Schema {
+
+ /**
+ * Recursively sets the ID of all nested JSON schemas to null.
+ * This method traverses through object schemas and array schemas, setting their ID properties to null.
+ *
+ * @param schema The root JsonSchema object to be processed.
+ /**
+ * Recursively sets the ID of all nested JSON schemas to null.
+ * This method traverses through object schemas and array schemas, setting their ID properties to null.
+ *
+ * @param schema The root JsonSchema object to be processed.
+ * @return The input JsonSchema with all nested schemas' IDs set to null.
+ */
+ public static JsonSchema setIdNull(JsonSchema schema) {
+ if (schema.isObjectSchema()) {
+ for (String propertyKey : schema.asObjectSchema().getProperties().keySet()) {
+ JsonSchema childSchema = schema.asObjectSchema().getProperties().get(propertyKey);
+ if (childSchema.isObjectSchema())
+ setIdNull(childSchema);
+
+ if (childSchema.isArraySchema())
+ setIdNull(childSchema.asArraySchema().getItems().asSingleItems().getSchema());
+
+ schema.setId(null);
+ schema.set$ref(null);
+ }
+ }
+ return schema;
+ }
+
+ /**
+ * Generates a JSON schema for the given class, with the option to specify required fields.
+ * This method uses the Jackson library to generate the schema and customize it based on the provided required fields.
+ * It sets the ID of the schema and its nested schemas to null and adds the required fields to the schema's "required" property.
+ *
+ * @param clazz The class for which the JSON schema should be generated.
+ * @param requiredFields A varargs array of field names that should be marked as "required" in the schema.
+ * @return A JsonNode representing the generated schema, or null if an exception occurs during generation.
+ */
+ public static JsonNode getJsonNodeFor(Class> clazz, String... requiredFields) {
+ JsonSchema schema = generateSchema(clazz);
+ assert schema != null;
+ schema.setId(null);
+ return addRequiredFields(schema, requiredFields);
+ }
+
+ /**
+ * Generates a JSON schema for the given class, with the option to specify required fields.
+ * This method uses the Jackson library to generate the schema.
+ * It sets the ID of the schema and its nested schemas to null.
+ *
+ * @param clazz The class for which the JSON schema should be generated.
+ * @return A JsonNode representing the generated schema, or null if an exception occurs during generation.
+ */
+ public static JsonNode getJsonNodeFor(Class> clazz) {
+ JsonSchema schema = generateSchema(clazz);
+ assert schema != null;
+ schema.setId(null);
+ return mapper.valueToTree(schema);
+ }
+
+ /**
+ * Generates a JSON schema for the given class, with the option to specify required fields.
+ * This method uses the Jackson library to generate the schema and customize it based on the provided required fields.
+ * It sets the ID of the schema and its nested schemas to null and adds the required fields to the schema's "required" property.
+ *
+ * @param clazz The class for which the JSON schema should be generated.
+ * @return A JsonNode representing the generated schema, or null if an exception occurs during generation.
+ */
+ public static JsonSchema generateSchema(Class> clazz) {
+ try {
+ JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(new ObjectMapper());
+ return setIdNull(schemaGen.generateSchema(clazz));
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Adds the specified required fields to the "required" property of a JSON schema.
+ * This method adds each field name from the provided array to the "required" property of the schema.
+ *
+ * @param schema The JSON schema to modify.
+ * @param requiredFields An array of field names that should be marked as required in the schema.
+ * @return
+ */
+ private static JsonNode addRequiredFields(JsonSchema schema, String[] requiredFields) {
+ JsonNode schemaNode = mapper.valueToTree(schema);
+
+ if (!(schemaNode instanceof ObjectNode root)) {
+ throw new IllegalArgumentException("Schema must be an ObjectNode");
+ }
+
+ ArrayNode required = root.withArray("required");
+
+ for (String fieldName : requiredFields) {
+ required.add(fieldName);
+ }
+
+ // Convert the modified JsonNode back to JsonSchema
+ return mapper.valueToTree(root);
+ }
+
+ }
}
}
+
diff --git a/src/test/java/AppTest.java b/src/test/java/AppTest.java
index 84b17a2..90750fd 100644
--- a/src/test/java/AppTest.java
+++ b/src/test/java/AppTest.java
@@ -1,5 +1,6 @@
import collections.Pair;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.google.gson.JsonObject;
import context.ContextStore;
import enums.ZoneIds;
@@ -280,4 +281,14 @@ public void partitionCountTest() {
);
printer.success("The partitionCountTest() test pass!");
}
+
+ @Test
+ public void jsonSchemaTest() {
+ JsonNode petSchema = MappingUtilities.Json.Schema.getJsonNodeFor(Pet.class);
+ Assert.assertEquals(
+ "Generated json schema did not match the expected one!",
+ "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"category\":{\"type\":\"any\"},\"name\":{\"type\":\"string\"},\"photoUrls\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"tags\":{\"type\":\"array\",\"items\":{\"type\":\"any\"}},\"status\":{\"type\":\"string\"}}}",
+ petSchema.toString()
+ );
+ }
}