All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsonschematools.schema.SchemaGenerator.scala Maven / Gradle / Ivy

package org.jsonschematools.schema

import org.json4s.*
import org.json4s.jackson.JsonMethods.*
import org.jsonschematools.schema.types.{ArrayType, JsonSchemaType, ObjectType}

/** Object responsible for generating JSON schema from a JSON string. */
object SchemaGenerator {

  /**
   * Generates a JSON schema from a JSON string.
   *
   * @param json
   *   The JSON string to generate schema from.
   * @return
   *   The JSON schema as a JValue.
   */
  def apply(json: String): JValue = new SchemaGenerator(parse(json)).toJValue()
}

/** Class responsible for generating JSON schema from a JSON AST (Abstract Syntax Tree). */
class SchemaGenerator(val bObj: JValue) {

  /**
   * Generates JSON schema from a given JSON AST.
   *
   * @param obj
   *   The JSON AST to generate schema from.
   * @param objId
   *   The ID of the current object.
   * @param firstLevel
   *   Indicates if it's the first level of recursion.
   * @param required
   *   Indicates whether properties are required.
   * @param nullable
   *   Indicates whether properties can be null.
   * @return
   *   The JSON schema as a JValue.
   */
  private def toJValue(
      obj: JValue = null,
      objId: String = null,
      firstLevel: Boolean = true,
      required: Boolean = true,
      nullable: Boolean = false): JValue = {

    // Determine the base object for schema generation
    val baseObject = if (firstLevel) bObj else obj

    // Builder for schema fields and required properties
    val schema = Seq.newBuilder[JField]
    val requiredProperties = Set.newBuilder[String]

    // Add meta fields for the top-level schema
    if (firstLevel) {
      schema += JField("$schema", JString(JsonSchemaType.schemaVersion))
      // schema += JField("id", JString("#"))
    }

    // Add object ID field if provided
    if (objId != null) schema += JField("id", JString(objId))

    // Determine the schema type of the base object
    val schemaType: JsonSchemaType = JsonSchemaType.getSchemaTypeFor(baseObject)
    schema += JField("type", JString(schemaType.toString))

    // Handle object properties
    baseObject match {
      case JObject(properties) if schemaType.isInstanceOf[ObjectType] =>
        // Handle each property of the object
        schema += JField(
          "properties",
          JObject(properties.map { case (name, elem) =>
            val propertySchema =
              toJValue(elem, null, firstLevel = false, required, nullable)
            if (required) requiredProperties += name
            name -> propertySchema
          }))

      // Handle array items
      case JArray(elems) if schemaType.isInstanceOf[ArrayType] && elems.nonEmpty =>
        val sameType = elems.forall(_.getClass == elems.head.getClass)
        val itemsSchema =
          if (sameType)
            JArray(List(toJValue(elems.head, null, firstLevel = false, required, nullable)))
          else
            JArray(
              elems.map(item => toJValue(item, null, firstLevel = false, required, nullable)))
        schema += JField("items", itemsSchema)

      case _ => // Do nothing
    }
    // Add required properties field if any
    if (requiredProperties.result().nonEmpty)
      schema += JField(
        "required",
        JArray(requiredProperties.result().toList.sorted.map(JString.apply)))
    // Convert the schema builder to a JObject and return
    JObject(schema.result().toList)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy