Code Monkey home page Code Monkey logo

json-kotlin-schema-codegen's Introduction

Stop the war in Ukraine

json-kotlin-schema-codegen

Build Status License: MIT Kotlin Maven Central

Code generation for JSON Schema (Draft 07).

NEW

New in version 0.106 – the classNames configuration option has been extended to allow the configuration of generated nested class names. See classNames in the Configuration Guide for more details.

New in version 0.105 – the generator will optionally validate default and examples entries against the schema in which they appear. See the examplesValidationOption and defaultValidationOption section in the Configuration Guide.

New in version 0.100 – the generator will produce classes that handle additionalProperties and patternProperties. See the additionalProperties and patternProperties guide for more details.

New in version 0.87 – the generator now recognises a special case of anyOf or oneOf to specify nullability. See below.

New in version 0.84 – the generator will now recognise the not schema for most validations, and will output reversed validation checks. For example, "not": { "const": "error" } in a property sub-schema will test that a string is not equal to "error".

Added to the code generator – the ability to configure generation options using a JSON or YAML file. See the documentation at CONFIG.md.

Also build tool support – see below.

Also, the ability to add annotations to generated classes – see annotations.

And from version 0.86 onward, the ability to force the output of a companion object for all or selected classes – see companionObject.

Background

JSON Schema provides a means of describing JSON values – the properties of an object, constraints on values etc. – in considerable detail. Many APIs now use JSON Schema to specify the content of JSON parameters and response objects, either directly or as part of an OpenAPI specification, and one way of ensuring conformity to the specified schema is to generate code directly from the schema itself.

This is not always possible – some characteristics described by a schema may not be representable in the implementation language. But for a large subset of schema definitions a viable code representation is achievable, and this library attempts to provide conversions for the broadest possible range of JSON Schema definitions.

The library uses a template mechanism (employing Mustache templates), and templates are provided to generate classes in Kotlin and Java, or interfaces in TypeScript.

Quick Start

Simply create a CodeGenerator, supply it with details like destination directory and package name, and invoke the generation process:

        val codeGenerator = CodeGenerator()
        codeGenerator.baseDirectoryName = "output/directory"
        codeGenerator.basePackageName = "com.example"
        codeGenerator.generate(File("/path/to/example.schema.json"))

The resulting code will be something like this (assuming the schema is the one referred to in the introduction to json-kotlin-schema):

package com.example

import java.math.BigDecimal

data class Test(
    /** Product identifier */
    val id: BigDecimal,
    /** Name of the product */
    val name: String,
    val price: BigDecimal,
    val tags: List<String>? = null,
    val stock: Stock? = null
) {

    init {
        require(price >= cg_dec0) { "price < minimum 0 - $price" }
    }

    data class Stock(
        val warehouse: BigDecimal? = null,
        val retail: BigDecimal? = null
    )

    companion object {
        private val cg_dec0 = BigDecimal.ZERO
    }

}

Some points to note:

  • the generated class is an immutable value object (in Java, getters are generated but not setters)
  • validations in the JSON Schema become initialisation checks in Kotlin
  • nested objects are converted to Kotlin nested classes (or Java static nested classes)
  • fields of type number are implemented as BigDecimal (there is insufficient information in the schema in this case to allow the field to be considered an Int or Long)
  • non-required fields may be nullable and may be omitted from the constructor (the inclusion of null in the type array will allow a field to be nullable, but not to be omitted from the constructor)
  • a description will be converted to a KDoc comment in the generated code if available

Multiple Files

The Code Generator can process a single file or multiple files in one invocation. The generate() function takes either a List or a vararg parameter array, and each item may be a file or a directory. In the latter case all files in the directory with filenames ending .json or .yaml (or .yml) will be processed.

It is preferable to process multiple files in this way because the code generator can create references to other classes that it knows about – that is, classes generated in the same run. For example, if a properties entry consists of a $ref pointing to a schema that is in the list of files to be generated, then a reference to an object of that type will be generated (instead of a nested class).

Clean Code

It is important to note that the output code will be "clean" – that is, it will not contain annotations or other references to external libraries. I recommend the use of the kjson library for JSON serialisation and deserialisation, but the classes generated by this library should be capable of being processed by the library of your choice.

There is one exception to this – classes containing properties subject to "format" validations will in some cases (e.g. "email", "ipv4") cause references to the json-validation library to be generated, and that library must be included in the build of the generated code.

additionalProperties

The default in the JSON Schema specification for additionalProperties is true, meaning that any additional properties in an object will be accepted without validation. Many schema designers will be happy with this default, or will even explicitly specify true, so that future extensions to the schema will not cause existing uses to have problems.

Unfortunately, there is no straightforward implementation for additionalProperties in code generation, so the setting will be taken as false even if it is specified otherwise. Most JSON deserialisation libraries have a means of specifying that additional properties are to be ignored; for kjson the allowExtra variable (Boolean) in JSONConfig must be set to true.

data class

The code generator will create a data class whenever possible. This has a number of advantages, including the automatic provision of equals and hashCode functions, keeping the generated code as concise and readable as possible.

Unfortunately, it is not always possible to use a data class. When the generated code involves inheritance, with one class extending another, the base class will be generated as an open class and the derived class as a plain class.

In these cases the code generator will supply the missing functions – equals, hashCode, toString, copy and the component[n] functions that would otherwise be created automatically for a data class.

Nullability

The standard way of specifying a value as nullable in JSON Schema is to use the type keyword:

    { "type": [ "object", "null" ] }

When the code generator encounters a property or array item defined in this way, it will make the generated type nullable.

A problem arises when we consider the interaction of this declaration of nullability with the required keyword. What should the generator produce for an object property that does not include null as a possible type, but is not in the required list? The solution adopted by the code generator is to treat the property as if it had been defined to allow null, and this seems to work well for the majority of cases, although strictly speaking, it is not an accurate reflection of the schema.

In particular, it helps with the case of utility sub-schema which is included by means of $ref in multiple places, in some cases nullable and in some cases not. For example, an invoice may have a billing address and an optional delivery address, both of which follow a common pattern defined in its own schema. The shared definition will have "type": "object", but the delivery address will need to be nullable, so generating a nullable type for a reference omitted from the required list will have the desired effect.

But this solution does not work for all circumstances. For example, it does not cover the case of an included sub-schema as an array item – there is no required for array items.

One way of specifying such a schema using the full capabilities of JSON Schema is as follows:

{
  "type": "object",
  "properties": {
    "billingAddress": {
      "$ref": "http://example.com/schema/address"
    },
    "deliveryAddress": {
      "anyOf": [
        { "$ref": "http://example.com/schema/address" },
        { "type": "null" }
      ]
    }
  },
  "required": [ "billingAddress", "deliveryAddress" ]
}

It is not easy to generate code for the general case of oneOf or anyOf, but the code generator will detect this specific case to output the deliveryAddress as nullable:

  1. The anyOf or oneOf array must have exactly two sub-schema items
  2. One of the items must be just { "type": "null" }

In this case, the code generator will generate code for the other sub-schema item (the one that is not { "type": "null" }, often a $ref), and treat the result as nullable.

Custom Classes

(NOTE – the configuration file may be a simpler way to specify custom classes, particularly when combined with other configuration options. See the Configuration Guide.)

The code generator can use custom types for properties and array items. This can be valuable when, for example, an organisation has its own custom classes for what are sometimes called "domain primitives" – value objects representing a fundamental concept for the functional area.

A common example of a domain primitive is a class to hold a money value, taking a String in its constructor and storing the value as either a Long of cents or a BigDecimal.

There are three ways of specifying a custom class to the code generator:

  1. URI
  2. Custom format types
  3. Custom keywords

URI

An individual item in a schema may be nominated by the URI of the element itself. For example, in the schema mentioned in the Quick Start section there is a field named price. To specify that the code generator is to use a Money class for this field, use:

        codeGenerator.addCustomClassByURI(URI("http://pwall.net/test#/properties/price"), "com.example.Money")

The base URI can be either the URL used to locate the schema file or the URI in the $id of the schema.

A distinct advantage of this technique is that when a $ref is used to share a common definition of a field type, the destination of the $ref can be specified to the code generator function shown above, and all references to it will use the nominated class. It is also the least obtrusive approach – it does not require modification to the schema or non-standard syntax.

Format

The JSON Schema specification allows for non-standard format types. For example, if the specification of the property in the schema contained "format": "x-local-money", then the following will cause the property to use a custom class:

        codeGenerator.addCustomClassByFormat("x-local-money", "com.example.Money")

Custom Keyword

The JSON Schema specification also allows for completely non-standard keywords. For example, the schema could contain "x-local-type": "money", in which case the following would invoke the use of the custom class:

        codeGenerator.addCustomClassByExtension("x-local-type", "money", "com.example.Money")

JSON Schema Version

This code generator targets the Draft-07 of the JSON Schema specification, and it includes some features from Draft 2019-09.

It also includes support for the int32 and int64 format types from the OpenAPI 3.0 Specification.

API Reference

A CodeGenerator object is used to perform the generation. It takes a number of parameters, many of which can be specified either as constructor parameters or by modifying variables in the constructed instance.

Parameters

  • targetLanguage – a TargetLanguage enum specifying the target language for code generation (the options are KOTLIN, JAVA or TYPESCRIPT – TypeScript coverage is not as advanced as that of the others at this time)
  • templateName – the primary template to use for the generation of a class
  • enumTemplateName – the primary template to use for the generation of an enum
  • basePackageName – the base package name for the generated classes (if directories are supplied to the generate() function, the subdirectory names are used as sub-package names)
  • baseDirectoryName – the base directory to use for generated output (in line with the Java convention, output directory structure will follow the package structure)
  • derivePackageFromStructure – a boolean flag (default true) to indicate that generated code for schema files in subdirectories are to be output to sub-packages following the same structure
  • generatorComment – a comment to add to the header of generated files
  • markerInterface – a “marker” interface to be added to every class

Functions

configure()

The configure() function takes a File or Path specifying a configuration file. See CONFIG.md for details of the configuration options.

generate()

There are two generate() functions, one taking a List of Files, the other taking a vararg list of File arguments. As described above, it is helpful to supply all the schema objects to be generated in a single operation.

generateClass(), generateClasses()

While the generate() functions take a file or files and convert them to an internal form before generating code, the generateClass() and generateClasses() functions take pre-parsed schema objects. This can be valuable in cases like an OpenAPI file which contains a set of schema definitions embedded in another file.

generateAll()

The generateAll() function allows the use of a composite file such as an OpenAPI file containing several schema definitions. For example, an OpenAPI file will typically have a components section which contains definitions of the objects input to or output from the API. Using the generateAll() function, the set of definitions can be selected (and optionally filtered) and the classes generated for each of them.

Build Tool Support

To simplify the use of the code generator in conjunction with the common build tools the following plugins will perform code generation as a pre-pass to the build of a project, allowing classes to be generated and compiled in a single operation:

Dependency Specification

The latest version of the library is 0.107, and it may be obtained from the Maven Central repository.

Maven

    <dependency>
      <groupId>net.pwall.json</groupId>
      <artifactId>json-kotlin-schema-codegen</artifactId>
      <version>0.107</version>
    </dependency>

Gradle

    implementation 'net.pwall.json:json-kotlin-schema-codegen:0.107'

Gradle (kts)

    implementation("net.pwall.json:json-kotlin-schema-codegen:0.107")

Peter Wall

2024-03-11

json-kotlin-schema-codegen's People

Contributors

ehowarth avatar pwall567 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

json-kotlin-schema-codegen's Issues

Enum property naming

Hi,

The code generator looks great. Thanks for the library 👍

I'm trying to achieve something but couldn't find a way and wondering if there is a support for this already.

Given the following json schema

{
  "$schema": "http://json-schema.org/draft/2019-09/schema",
  "$id": "http://enum-schema.net/test-enum",
  "description": "Test enum.",
  "type": "string",
  "enum": [
    "first",
    "second",
    "third"
  ]
}

I'm expecting the following code generated.

import com.fasterxml.jackson.annotation.JsonProperty

enum class TestEnum(private val value: String) {
  @JsonProperty("first") FIRST("first"),
  @JsonProperty("second") SECOND("second"),
  @JsonProperty("third") THIRD("third");
}

Specifically, I would like to

  • See the enum property to be a uppercase name (ex: FIRST) in the generated code.
  • However, when this enum is serialized as a json string, it will need to be a lowercased string (ex: first).

Hi @pwall567, Is there a way I can configure the json-kotlin-schema-codegen to achieve this?

[0.81] CustomClasses doesn't support generic types

With the following property definition:

"repositories": {
	"description": "The list of repositories being sourced.",
	"type": "object",
	"format": "map",
	"additionalProperties": {
		"$ref": "https://aetheric.co.nz/schema/gsm/repository"
	}
}

and the following config:

"customClasses": {
	"format": {
		"map": "kotlin.collections.Map"
	}
}

I end up with:

import kotlin.collections.Map

data class Settings(
    val repositories: Map? = null
)

which is invalid.

If I add the generic signature to the type, it includes it in the import, which is also invalid.

I worked around the issue by creating a typealias and mapping to that, but I can see two ways to improve the experience:

  • Strip off generic typing on the import declaration.
  • Make object types without an explicit name or $id default to Map<String,?> where ? is the common property type.

Support annotations on generated classes

It would be useful to support annotating the generated classes, like you already support markerInterface.

I would like to apply a list of annotations, like @kotlinx.serialization.Serializable and @javax.annotation.processing.Generated

[0.81] Annotations config not being applied to generation of data classes

When I have the following annotation config:

"annotations": {
	"classes": {
		"kotlinx.serialization.Serializable": null,
		"javax.annotation.Generated": "\"{{&generator}}\", date=\"{{&dateTime}}\""
	}
},

the generated classes don't have any annotations applied to them.

Note: I haven't tested this with non-data classes, nor fields.

Unable to generate classes from opds2 spec

With the following code

val codeGenerator = CodeGenerator()
codeGenerator.baseDirectoryName = "output/directory"
codeGenerator.basePackageName = "com.example"
codeGenerator.generate(URI("https://raw.githubusercontent.com/opds-community/drafts/master/schema/feed.schema.json"))

I get the following exception:

Exception in thread "main" net.pwall.json.pointer.JSONPointerException: Recursive $ref - 
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:175)
	at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:327)
        …

I do understand that maybe there are loops somewhere in refs, but I don't understand where. It would be very helpful to add the information about the loop into the error

Support for const (and/or single-value enums) in polymorphism

Thanks for sharing this project!

Would it be possible to support const or single-value enum as just pre-set value without constructor parameter?

Usecase: We're thinking about using this in our project, but we need to figure out how to support polymorphism in at least semi-intuitive way. I'm thinking the support of const would make the first step.

E.g., this jsonschema:

title: Something Changed Event
allOf:
  - $ref: "Event.yaml"
  - $ref: "SomethingFields.yaml"
properties:
  type:
    const: "SomethingChangedEvent"
    default: "SomethingChangedEvent"

Along with this referenced Event.yaml parent:

title: Event
type: object
required:
  - type
properties:
  type:
    type: string

Currently generate this child:

class SomethingChangedEvent(
    type: String = "SomethingChangedEvent",
    val other: Other
) : Event(type)

But this would be more error-proof if const would result in this child being generated:

class SomethingChangedEvent(
    val other: Other
) : Event(type = "SomethingChangedEvent")

Regarding the usecase, one possible next step for us would be to use Jackson2 mixins to also (manually) add polymorphic deserialization support based on this.

I'm not sure how this should work without polymorphism involved, though - perhaps type in this case could be just val field that is not present in constructor parameters? Is this reasonable?

Typescript imports are invalid when schema exist in different directories

If you have schema files in different directories, the generated Kotlin code (and I assume Java) works as expected. So a structure like:

# tree schema
schema
├── common
│   └── header.schema.json
└── messages
    └── chat-message.schema.json

With the Gradle plugin pointing at schema directory produces two files com/package/name/common/Header.kt and com/package/name/message/ChatMessage.kt with the latter correctly importing Header with import com.package.name.common.Header. TypeScript classes have the same (correct) layout but with import statements that assume all classes are in the same directory:import { Header} from "./Header". Unfortunately, there doesn't seem to be a magical set of package.json/tsconfig.json options that fixes module resolution.

I doubt my project will ever actually have multiple classes with the same name, so I just moved everything to the same directory/package. It would still be nice to fix this if only so for feature parity between the target languages.

Kotlin's mustache templates use fully resolved imports and TypeScript does not. It looks like the imports list is the fully qualified Java class name so it can't be reused for TypeScript.

I'm new to TypeScript and I'm not sure what's the best way for the generated files to refer to each other. My inclination is:

  1. Store imports in Target.kt as both a list of fully qualified class name strings and as a list of lists; where each sublist is each package in the fully qualified class (i.e. ["com.package.name.common.Header"] and [["com", "package", "name", "common", "Header"]]).
  2. Add an import prefix configuration setting for TypeScript files. I expect setting a custom import prefix pointing to the base directory of the generated files. Combine this configuration value and the sublist with '/' as the import path.
    • If set to #schema generate imports as import { Header} from "#schema/common/Header" and would require something like { "imports": { "#schema": { "import": "path/to/where/generated/typescript/lives" } } } in package.json.
    • Let's also say if unset, the existing import behavior is used. If your schema all live in the same package nothing changes; if in different packages you need to change your configuration.
  3. Alternatively, store the fully qualified path of each target and compute the relative paths between each itself and its imports. That's not a complex algorithm, but it is more complicated than using quasi-absolute paths of 2. However, it would not require any configuration by the user.

Missing imports when using `byExtension` and `allOf`

If we have a base model in file base.yaml:

type: object
properties:
  timestamp:
    type: string
    format: date-time

Then a model in file sub.json that uses this via allOf

{
    "type": "object",
    "allOf" : [
        { "$ref" : "base.yaml"}
    ],
    "properties": {
        "prop1" : {
            "type" : "string"
        }
    }
}

with gradle config:

configure<JSONSchemaCodegen> {
    packageName.set("com.exampe")
    classMappings {
        byFormat("java.time.Instant", "date-time")
    }
}

We correctly get:

package com.example

import java.time.Instant

data class Base(
    val timestamp: Instant? = null
)

But incorrectly get:

package com.example

class Sub(
    timestamp: Instant? = null,
    val prop1: String? = null
) : Base(timestamp) {
  ...
}

The issue is that class Sub is missing import java.time.Instant.

Custom template

Great work with both code generator and gradle plugin!

Just wanted to check if you have any plans for supplying a custom (mustache) templates, e.g. for class generation, and preferably also support in the gradle plugin?

Thanks!

Make use of format to infer type of generated fields

The generator only uses the type field to determine the type of the generated field in a class.

For example this property:

    "createdAt": {
      "type": "string",
      "description": "The timestamp in milliseconds when the account was created.",
      "format": "int64"
    }

will generate the field val createdAt:String despite the format giving the hint, that it should actually be interpreted as an integer. The same issue arises when a field is declared with "type":"number" while having "format":"int64". The generated classes use BigDecimal in this case, while a Long would be way more convenient and can be ensured to work due to the format.

Note: int64 can be replaced with int32 without changing the issue.

Schema parsing doesn't support whitespace inside keys

I came here after trying to parse the VGMDB.info API schema here, using pwall567/json-kotlin-gradle, but it fails because some of the definitions have spaces in their keys.

Specifically if you check at https://vgmdb.info/schema/artist.json under definitions.artistInfo, which contains a property called Album Votes.

I can't find an explicit reference in the latest JSON Schema version that keys must not contain whitespace. Only that they must be strings and properly referenced using "key": value. So I think this should be supported, although obviously the whitespace will have to be replaced with a _ or simply removed when converted to a Java reference. I'm also not sure if whatever JSON parser you're using would support reading the key with whitespace.

For reference, this is the entire meaningful stacktrace:

Caused by: java.lang.IllegalArgumentException: Illegal character in fragment at index 41: #/definitions/artistInfo/properties/Album Votes
        at net.pwall.json.schema.codegen.CodeGenerator.findCustomClass(CodeGenerator.kt:1098)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseProperty(CodeGenerator.kt:1113)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseProperties(CodeGenerator.kt:1053)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseObject(CodeGenerator.kt:832)
        at net.pwall.json.schema.codegen.CodeGenerator.findTargetClass(CodeGenerator.kt:1089)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseProperty(CodeGenerator.kt:1130)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseProperties(CodeGenerator.kt:1053)
        at net.pwall.json.schema.codegen.CodeGenerator.analyseObject(CodeGenerator.kt:832)
        at net.pwall.json.schema.codegen.CodeGenerator.processTargetCrossReferences(CodeGenerator.kt:569)
        at net.pwall.json.schema.codegen.CodeGenerator.generateAllTargets(CodeGenerator.kt:538)
        at net.pwall.json.kotlin.codegen.gradle.JSONSchemaCodegenTask.generate(JSONSchemaCodegenTask.kt:93)

It looks like it's caused by the fact that java.net.URI doesn't support spaces in the fragment, which makes sense. This code would have to do regular string comparison, although I didn't dive far enough into the specifics to understand how difficult that will be.

On a tangential note, the only reason I'm trying to do this is because there's no VGMDB Kotlin wrapper, and also no RAML->Kotlin generator, so if anyone has any suggestions on that front, then this issue wouldn't matter.

Compile error on arrays of objects with `additionalPropertiesOption: strict`

At compile-time we see the following when setting additionalPropertiesOption: strict in the config:

Cannot check for instance of erased type: List<Bar.Foo>

For a schema containing an array of objects:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "bar.schema.json",
  "properties": {
    "foos": {
      "type": "array",
      "items": {
        "id": "foo",
        "type": "object"
      }
    }
  }
}

The offending generated code in Bar.kt:

if (cg_map.containsKey("foos"))
  require(cg_map["foos"] is List<Foo>) { "foos is not the correct type, expecting List<Foo>?" }

This is a blocker for us, since our schema required pattern properties, thus iiuc we need to enable additionalPropertiesOption: strict for the relevant code the generated.

Applying via json-kotlin-gradle plugin 0.102.

@pwall567 Do you have bandwidth to provide a fix? 🙏

Representing `object`s with arbitrary keys and values

Defining "type": "object" in JSON Schema always causes an open class to be generated, even when no properties are defined. How do we represent a plain Map, where property keys and values are determined at runtime?

Here's what I have now:

Example schema:

{
  "$id": "https://groundplatform.org/loi-document.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Location of Interest",
  "type": "object",
  "properties": {
    "foo": {
      "type": "object",
      "additionalProperties": true
    }
  }
}

Note: "additionalProperties": true is the default in JSON Schema, including it here for clarity.

The generated code:

data class LoiDocument(
    val foo: Foo? = null,
) {
    open class Foo
}

"Unexpected default value" with JSONZero type

For build.gradle.kts file:

import net.pwall.json.kotlin.codegen.gradle.JSONSchemaCodegenPlugin
import net.pwall.json.kotlin.codegen.gradle.JSONSchemaCodegen

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath("net.pwall.json:json-kotlin-gradle:0.84")
    }
}

apply<JSONSchemaCodegenPlugin>()

val baseGLTFUrl = "https://raw.githubusercontent.com/KhronosGroup/glTF/main/specification/2.0/schema/"

val gltfSchemasNames = arrayOf(
    "glTF"
)

configure<JSONSchemaCodegen> {
    inputs {
        for (gltfSchemaName in gltfSchemasNames) {
            val inputURI = uri("$baseGLTFUrl/$gltfSchemaName.schema.json")
            inputURI(uri(inputURI))
        }
    }
    language.set("kotlin")
}

(schema is here).

Running this, I see processDefaultValue calling

    private fun processDefaultValue(value: JSONValue?): Constraints.DefaultPropertyValue =
            when (value) {
                null -> Constraints.DefaultPropertyValue(null, JSONSchema.Type.NULL)
                is JSONInteger -> Constraints.DefaultPropertyValue(value.value, JSONSchema.Type.INTEGER)
                is JSONString -> Constraints.DefaultPropertyValue(StringValue(value.value), JSONSchema.Type.STRING)
                is JSONBoolean -> Constraints.DefaultPropertyValue(value.value, JSONSchema.Type.BOOLEAN)
                is JSONSequence<*> -> Constraints.DefaultPropertyValue(value.map { processDefaultValue(it) },
                        JSONSchema.Type.ARRAY)
                is JSONMapping<*> -> fatal("Can't handle object as default value")
                else -> fatal("Unexpected default value") // this line is being called
            }

with value type as JSONZero causing the generator to fail. For reference, the schema trying to access is https://raw.githubusercontent.com/KhronosGroup/glTF/main/specification/2.0/schema/accessor.schema.json with location /properties/byteOffset/default

URNs as schema ids

Hi there,

I really like your project. But I’m having some issues with my schemas.

Are you planning to support URNs as schema ids? Currently this results in the class name Urn.

Example:

{
    "id": "urn:jsonschema:com:example:Person",
…
}

Thanks!

Prevent imported types containing generic types

If a class mapping is performed with generics, for example

configure<JSONSchemaCodegen> {
    packageName.set("com.example")
    classMappings {
        byExtension("kotlin.collections.Map<String, Any?>", "x-polymorphic", "true")
    }
}

Then the resulting import in the generated code looks like:

package com.example

import kotlin.collections.Map<String, Any?>

data class Example(
   ...
)

I think it might be an easy fix to change the following function in Target from:

    fun addImport(classId: ClassId) {
        if (!samePackage(classId))
            imports.addOnce(classId.qualifiedClassName)
        localImports.addOnce(classId)
    }

to:

    fun addImport(classId: ClassId) {
        if (!samePackage(classId))
            imports.addOnce(classId.qualifiedClassName.substringBefore('<'))
        localImports.addOnce(classId)
    }

Alternatively, this could be changed to be stripped off when processing the className within the various CustomClass sublcass constructors.

Invalid Generated code (conflicting declaration)

Hi,

Let me start by thanking you for the good work !!

Now, I have a use case where the generated code does not compile.
Given the schema:

{ "$schema": "http://json-schema.org/draft-04/schema#", "title": "Commodities.Forward.Forward.InstRefDataReporting.V1", "LastModifyDateTime": "2020-08-05T10:37:37", "type": "object", "properties": { "Header": { "title": "Header", "type": "object", "properties": { "AssetClass": { "title": "Asset Class", "description": "As defined by CFI code: ISO 10962 (2015); Character #2", "type": "string", "enum": [ "Commodities" ] } }, }, }, }

The generated code looks like:
`
data class CommoditiesForwardForwardInstRefDataReportingV1(
val Header: Header,
) {

data class Header(
    val AssetClass: AssetClass
)

enum class AssetClass {
Commodities
}
}
`

which does not compile with error "conflicting declaration" because the field name == class name

Invalid code generated for `patternProperties` containing objects

For example:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "foo.schema.json",
  "type": "object",
  "patternProperties": {
    "abc": {
      "type": "object",
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}

Generated code:

    init {
        cg_map.keys.forEach { key ->
            if (!.matches(key))
                throw IllegalArgumentException("Unexpected field $key")
        }
    }

Compile error:

e: file:///...Foo.kt:18:18 Expecting an element

Running through Gradle plugin v0.102 with "additionalPropertiesOption": "strict" in config.

@pwall567 Any thoughts?

Add a default empty companion for extension

It would be helpful if you provided a default companion object {} to the generated classes so that we could write static extension methods (like various constructors or some converters, etc..).

Option for not setting `null` as default value for non-required fields

Hi. When code is generated from properties which are non-required, the default value is automatically set to null. I would like to not generate any default value for these fields. Is there a way to do this? If not, is there a possibility to add this functionality?

The practical problem for me is that when dealing with multiple models and mappers, the default value allows for making mistakes more easily when writing the mappers.

Example:

Generated code now:

data class Data(
    val otherField: String,
    val nonrequired: String? = null
)

But I would prefer:

data class Data(
    val otherField: String,
    val nonrequired: String?
)

Bug: target class name does not get updated after first schema

Hi,

While using the Gradle plugin based on this project, I encountered a weird bug in which the target class name gets "stuck" after processing the first of a number of JSON schemas in a directory, leading to each subsequent schema overwriting the previous one, resulting in only a single remaining generated class.

This happens with version 0.92 of both json-kotlin-schema-codegen and version 0.91 of json-kotlin-gradle. I also tried an earlier version of json-kotlin-gradle (I believe I randomly picked 0.87), and the issue existed there as well.

Code to reproduce:

/*
 * This Kotlin source file was generated by the Gradle 'init' task.
 */
package reproducebug

import net.pwall.json.schema.codegen.CodeGenerator
import java.io.File

class App {
    val greeting: String
        get() {
            return "Hello World!"
        }
}

fun main() {
    println(App().greeting)
    val codeGenerator = CodeGenerator()
    codeGenerator.baseDirectoryName = "./generated-sources"
    codeGenerator.basePackageName = "com.example"
    codeGenerator.generate(File(checkNotNull(App::class.java.classLoader.getResource("OCPP-2.0.1_part3_JSON_schemas")).path))
}

the JSON schemas that this code expects in src/main/resources/OCPP-2.0.1_part3_JSON_schemas can be downloaded from the website of the Open Charge Alliance. Free registration is required, and I'm not sure if I can just share the schemas here. You need to select "OCPP 2.0.1 (all files)". and will receive a download link. The ZIP file OCPP-2.0.1_-all_files_1.zip will in turn contain the ZIP file OCPP-2.0.1_part3_JSON_schemas, which contains these JSON schemas.

I'll try to get a clarification whether I'm allowed to immediately share the JSON schemas here as an attachment.

Anyway, once you run this code on these JSON schemas, the output will be as follows (I'm just showing the first couple of lines, because that's enough to see what's going wrong):

Hello World!
15:54:18.819 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/NotifyEVChargingNeedsRequest.json
15:54:18.821 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.182 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/GetLocalListVersionResponse.json
15:54:19.182 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.190 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/ClearChargingProfileResponse.json
15:54:19.191 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.197 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/DeleteCertificateRequest.json
15:54:19.197 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.203 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/UnlockConnectorResponse.json
15:54:19.203 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.208 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/GetVariablesResponse.json
15:54:19.208 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.213 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/UnlockConnectorRequest.json
15:54:19.213 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.294 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/GetLogResponse.json
15:54:19.295 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.303 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/AuthorizeRequest.json
15:54:19.304 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.311 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/HeartbeatResponse.json
15:54:19.312 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
15:54:19.317 INFO  ...all.json.schema.codegen.CodeGenerator: Generating for $HOME/poc/reproducebug/app/build/resources/main/OCPP-2.0.1_part3_JSON_schemas/ClearCacheResponse.json
15:54:19.317 INFO  ...all.json.schema.codegen.CodeGenerator: -- target class com.example.NotifyEVChargingNeedsRequest
(...)

(I replaced my actual home folder with $HOME in this output.)

As you can see, the source file name gets updated properly each log line Generating for ..., but it remains stuck on the first one in each log line -- target class ....

And indeed, once the application finishes, only one file NotifyEVChargingNeedsRequest.kt ends up in the generated sources.

I briefly looked into the code, and my guess it that some kind of instance variable in CodeGenerator.kt does not seem to get updated properly. Possibly classNameMapping?

Anyway, let me know if I can offer assistance in solving this issue.

Thanks for sharing this very useful project with the world!

Support for anyOf null, object

I think there's an edge case of oneOf (#2) that can be implemented easily.

I converterted https://docs.github.com/en/rest/repos/repos#get-a-repository 's "Response Schema" and I got val license: Any instead of val license: License?

    "license": {
      "anyOf": [
        {
          "type": "null"
        },
        {
          "title": "License Simple",
          "description": "License Simple",
          "type": "object",
          "properties": {
            "key": {
              "type": "string",
              "examples": [
                "mit"
              ]
            },
            "name": {
              "type": "string",
              "examples": [
                "MIT License"
              ]
            },
            "url": {
              "type": [
                "string",
                "null"
              ],
              "format": "uri",
              "examples": [
                "https://api.github.com/licenses/mit"
              ]
            },
            "spdx_id": {
              "type": [
                "string",
                "null"
              ],
              "examples": [
                "MIT"
              ]
            },
            "node_id": {
              "type": "string",
              "examples": [
                "MDc6TGljZW5zZW1pdA=="
              ]
            },
            "html_url": {
              "type": "string",
              "format": "uri"
            }
          },
          "required": [
            "key",
            "name",
            "url",
            "spdx_id",
            "node_id"
          ]
        }
      ]
    },

Problem with nesting and annotations

Hi Peter,

first of all thank you for your great tool for generating kotlin data classes. I have a little problem using it atm. My Use Case is:

I want to use the maven plugin for generating kotlin data classes, which I can use together with kotlinx-serialization.

So I will need to annotate them. I found out how to do it with the config file (also great documentation btw.).

BUT The nested classes are not annotated and I don't know how to get the Plugin to not nest those (as they are liked via $ref). So I would need either a possibility to not nest (maybe by defining a sequence in the pom file?, or having a flag "nesting") or to annotate all nested classes as well.

Do you have a hint for me how to achieve this?

Regards and all the best

Christian

Code generation creates a class with suffix number for each reference to the same object.

Hi,

Good job with this tool. Why code generation creates two classes with suffix number if the reference is pointing to the same object?. Which is the reason?.

Also if would like to use other numeric data types like BigInt in our generated classes what would you suggest to do?

following this json example:

{
  "$schema": "http://json-schema.org/draft/2019-09/schema",
  "$id": "http://pwall.net/test",
  "title": "Product",
  "type": "object",
  "required": ["id", "name", "price"],
  "properties": {
    "id": {
      "type": "number",
      "description": "Product identifier"
    },
    "name": {
      "type": "string",
      "description": "Name of the product"
    },
    "price": {
      "type": "number"
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "stock": {
      "$ref": "#/definitions/Test"
    },
    "stock2": {
      "$ref": "#/definitions/Test"
    }
  },
  "definitions": {
    "Test": {
      "type": "object",
      "properties": {
        "warehouse": {
          "type": "number"
        },
        "retail": {
          "type": "number"
        }
      }
    }
  }
}

output:

package com.app

import java.math.BigDecimal

data class Test(
    /** Product identifier */
    val id: BigDecimal,
    /** Name of the product */
    val name: String,
    val price: BigDecimal,
    val tags: List<String>? = null,
    val stock: Test? = null,
    val stock2: Test1? = null
) {

    data class Test(
        val warehouse: BigDecimal? = null,
        val retail: BigDecimal? = null
    )

    data class Test1(
        val warehouse: BigDecimal? = null,
        val retail: BigDecimal? = null
    )

}

Thanks,

Arturo

Support for oneOf

Hi! Thanks for an awesome project!

I'm trying to use it currently and my specific schema uses oneOf quite a lot.

Are there any plans to support this?

Thanks!

Support more flexibility in creating class and field annotations

Currently, class and field annotation is relatively limited in what can be done, due to the fact that the generator is using the core mustache syntax. It would be awesome if it would be able to use some extended version (like handlebars) so that I could do things like check the class type when generating annotations:

{
  "annotations": {
    "classes": {
      "kotlinx.serialization.Serializable": "{{#if className=\"SomeClass\""}}with = SomeSerializer::class{{/if}}
    }
  }
}

The general problem i'm trying to solve is that I'm using kotlinx serialization, and i have a polymorphic structure like this:

interface Card

class HeaderCard: Card

class ListCard: Card

And i have a JsonContentPolymorphicSerializer to go along with it. The problem is that when i nest this card into another structure, i really need to have the Serializable annotation be very specific about which serializer to use, or it doesn't work, and i get an error about it not finding a class discriminator.

The simplest thing to do is to add the "with" clause onto the generated class, but i can't figure out a way to do so using the existing mustache support. Otherwise i have to construct a very specific json parsing instance, which is not entirely desirable.

        val module = SerializersModule {
            polymorphic(Card::class) {
                defaultDeserializer { CardSerializer }
            }
        }

        val format = Json { serializersModule = module }

Custom templates with custom conditionals can generate annotations

Hi, we use your tool frequently and like it very much.

I have a feature request. We need to be able to generate annotations in the code based on custom vendor extensions in the schema on certain field.
e.g by adding a certain attribute x-custom-attribute to a certain field, I would like to in the mustasche template have a conditional that if set, generates an annotation in the resulting kotlin code.

We are using your gradle plugin and in it I see no option to specifiy custom templates.

Please see this for reference

https://github.com/vitalyros/swagger-generate-custom-annotations

Thanks

net.pwall.json.pointer.JSONPointerException: Recursive $ref - /definitions/Extension for FHIR v4.0.1 schema

I wish to generate kotlin data classe for the FHIR v4.0.1 draft v7 json schema (download) and executing

    val codeGenerator = CodeGenerator()
    codeGenerator.baseDirectoryName = "/Users/xxxxxx/IdeaProjects/Generation/src/output"
    codeGenerator.basePackageName = "com.example"
    codeGenerator.generate(File("/Users/xxxxxx/IdeaProjects/Generation/src/resource/fhir.schema.json")) 

gives


SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" net.pwall.json.pointer.JSONPointerException: Recursive $ref - /definitions/Extension
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:175)
	at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:327)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202)
	at net.pwall.json.schema.parser.Parser.parseItems(Parser.kt:336)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:223)
	at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:342)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215)
	at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:327)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202)
	at net.pwall.json.schema.parser.Parser.parseItems(Parser.kt:336)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:223)
	at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:342)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215)
	at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:327)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202)
	at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:342)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215)
	at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:327)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202)
	at net.pwall.json.schema.parser.Parser.parseCombinationSchema(Parser.kt:309)
	at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:208)
	at net.pwall.json.schema.parser.Parser.parse(Parser.kt:149)
	at net.pwall.json.schema.parser.Parser.parse(Parser.kt:120)
	at net.pwall.json.schema.codegen.CodeGenerator.addTarget(CodeGenerator.kt:478)
	at net.pwall.json.schema.codegen.CodeGenerator.addTargets(CodeGenerator.kt:370)
	at net.pwall.json.schema.codegen.CodeGenerator.addTargets$default(CodeGenerator.kt:364)
	at net.pwall.json.schema.codegen.CodeGenerator.generate(CodeGenerator.kt:354)
	at net.pwall.json.schema.codegen.CodeGenerator.generate(CodeGenerator.kt:344)
	at Generate_FHIR_schemaKt.main(Generate_FHIR_schema.kt:11)

Gradle plugin?

Hi,

This looks great, and from first glance is just what I'm looking for! Are you planning on adding any kind of build system support soon? I'm happy to help.

Duplicate enum class documentation

Converting https://www.aosd.cloud.audi/jsonschemadoc/static/aosd.schema.json using the Kotlin script

#!/usr/bin/env kotlin

@file:DependsOn("net.pwall.json:json-kotlin-schema-codegen:0.106")

import java.io.File
import net.pwall.json.schema.codegen.CodeGenerator

val codeGenerator = CodeGenerator()
codeGenerator.baseDirectoryName = "output/directory"
codeGenerator.basePackageName = "com.example"
codeGenerator.generate(File("/home/sebastian/Downloads/aosd.schema.json"))

gives

    /**
     * Identifying the origin of the license information.
     */
    /**
     * Identifying the origin of the license information.
     */
    enum class Origin {
        packagemanagement,
        scm,
        licensefile
    }

so the enum class documentation is duplicated.

Confusion about nested classes

The documentation states

nested objects are converted to Kotlin nested classes (or Java static nested classes)

But it also states

For example, if a properties entry consists of a $ref pointing to a schema that is in the list of files to be generated, then a reference to an object of that type will be generated (instead of a nested class).

Now I have two files:

{
    "$schema": "http://json-schema.org/draft-04/schema",
    "additionalProperties": false,
    "definitions": {
        "typeA": {
            "type": "object",
            "properties": {
                "altitude": {
                    "type": "number"
                }
            }
        }
    },
    "$ref": "#/definitions/typeA"
}

and a second file

{
    "$schema": "http://json-schema.org/draft-04/schema",
    "type": "object",
    "properties": {
        "test": {
            "$ref": "rth.schema.json#/definitions/typeA"
        }
    },
    "additionalProperties": false
}

What is the supposed output of this? As quote one would suggest a nested class but quote two would suggest that the second file just uses a reference to the class from file one.

It results for me in this. So the class is created twice (with different name, one based on the property and one based on the filename).

data class TestSchema(
    val test: Test? = null
)  {

    data class Test(
        val altitude: BigDecimal? = null
    )

}
data class Rth(
    val altitude: BigDecimal? = null
)

Generate `typealias $className = Any` when type cannot be determined

Hi @pwall567 👋

We are generating a schema that will support any valid JSON and would love to have Kotlin codegen support for this.

As an example, consider the following Untyped type:

$defs:
  MyType:
    type: object
    properties:
      data:
        "$ref": "#/$defs/Untyped"
  Untyped:
    type:
    - array
    - boolean
    - integer
    - number
    - object
    - string

There is no error, but this simply fails to generate a data class for Untyped, and therefore the resulting Kotlin code does not compile.

In cases like this where a single type cannot be determined, one option is to generate a class with a typealias:

// Untyped.kt
typealias Untyped = Any

I have drafted a PR to demonstrate what we are looking for here: #27

What do you think?

JSONPointerException: Recursive $ref

When I try to generate classes from the FHIR Schema, I get following Exception:

Exception in thread "main" net.pwall.json.pointer.JSONPointerException: Recursive $ref - /definitions/Extension at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:175) at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:324) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202) at net.pwall.json.schema.parser.Parser.parseItems(Parser.kt:332) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:223) at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:338) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215) at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:324) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202) at net.pwall.json.schema.parser.Parser.parseItems(Parser.kt:332) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:223) at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:338) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215) at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:324) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202) at net.pwall.json.schema.parser.Parser.parseProperties(Parser.kt:338) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:215) at net.pwall.json.schema.parser.Parser.parseRef(Parser.kt:324) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:202) at net.pwall.json.schema.parser.Parser.parseCombinationSchema(Parser.kt:309) at net.pwall.json.schema.parser.Parser.parseSchema(Parser.kt:208) at net.pwall.json.schema.parser.Parser.parse(Parser.kt:149) at net.pwall.json.schema.parser.Parser.parse(Parser.kt:120) at net.pwall.json.schema.codegen.CodeGenerator.addTarget(CodeGenerator.kt:482) at net.pwall.json.schema.codegen.CodeGenerator.addTargets(CodeGenerator.kt:374) at net.pwall.json.schema.codegen.CodeGenerator.addTargets$default(CodeGenerator.kt:368) at net.pwall.json.schema.codegen.CodeGenerator.generate(CodeGenerator.kt:358) at net.pwall.json.schema.codegen.CodeGenerator.generate(CodeGenerator.kt:348)

Am I doing something wrong?

Possible to add Field meta Data ?

Hi - is it possible to extend the code generation to include some meta data so that generated model can be used to build data dictionary or run time behaviour ??

e.g.

@FeildMetaData(encrypted=true, blabla=[])
val someField : String

Thanks.

Schema that specifies long min/max results in uncompilable code

We have some JSON schemas that explicitly specify the min/max values for all numeric fields. In a few cases, the min/max are just the min/max of a Long. This library produces an initialization check like:

    init {
        require(someLongField in -9223372036854775808L..9223372036854775807L) { "someLongField not in range -9223372036854775808..9223372036854775807 - $someLongField" }
    }

However, this code does not compile, because while -9223372036854775808L is a valid Long value, Kotlin doesn't currently support it as a literal (since it compiles to 9223372036854775808L * -1 and 9223372036854775808L is larger than Long.MAX_VALUE).

For now, I'm working around this by replacing the long min/max values with a reference to the Long.MIN_VALUE and Long.MAX_VALUE constants, but it'd be nice to not have to work around this and it seems like a bug for this library to generate uncompilable code.

Could the library detect and work around this case? You could either replace the literals with constant references (as I'm doing) or maybe even omit the initialization check altogether--in this case, it's not possible to have a long value outside this range.

Kotlin Multiplatform support

I've noticed that fields like this end up using BigDecimal, which means they are not pure kotlin, and can't be used in multiplatform projects.

"type": "number",
"format": "decimal"

It also makes me jump through some hoops with my serializer configuration to be able to parse the BigDecimal properly within the jvm environment. It can be done, but i'd rather have a KMP friendly way of generating the models.

    val JsonSerializer by lazy {
        Json {
            serializersModule = SerializersModule {
                contextual(BigDecimal::class, BigDecimalSerializer)
            }
        }
    }

Either that, or i have to alter my configuration for the generator:

{
  "annotations": {
    "classes": {
      "kotlinx.serialization.Serializable": ""
    },
    "fields": {
      "kotlinx.serialization.Serializable": "{{#isDecimal}}with = BigDecimalSerializer::class{{/isDecimal}}"
    }
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.