jsonschema2pojo icon indicating copy to clipboard operation
jsonschema2pojo copied to clipboard

1.2.2 capitalized property names replaced with lowercase

Open shme7ev opened this issue 1 year ago • 3 comments

For capitalized property names, 1.2.1 generates properties with first lower-case letter and the rest is capitalized. 1.2.2 generates all-lower case property names. Is there a config option to control this behavior? example: 1.2.1:

    @JsonProperty("TU")
    public TU tU;

1.2.2:

    @JsonProperty("TU")
    public Tu tu;

shme7ev avatar Oct 08 '24 17:10 shme7ev

I'm quite surprised that this would change in 1.2.2 as I don't think there's any change that would cause it.

I can't replicate this myself when using 1.2.1 or 1.2.2. Do you have an example schema file or json snippet I can use?

joelittlejohn avatar Oct 08 '24 18:10 joelittlejohn

This snippet of schema generates "sNM" in 1.2.1 and "snm" in 1.2.2. The code I used for generating pojos follows.

        "SNM": {
            "type": "object",
            "properties": {
                "Surname": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                },
                "Name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                },
                "MiddleName": {
                    "type": "string"
                }
            },
            "additionalProperties": false
        },

import com.sun.codemodel.JCodeModel
import org.jsonschema2pojo.*
import org.jsonschema2pojo.rules.RuleFactory
import org.slf4j.LoggerFactory
import java.io.File
import java.net.URL

val log = LoggerFactory.getLogger("main")

private const val OUTPUT_DIR = "generated"

private const val PACKAGE = "ecp.schema.vs1"

fun main() {
    val codeModel = JCodeModel()

    val source: URL = CustomAnnotator::class.java.getResource("/schema/Vs1asci.json")!!

    val config: GenerationConfig = object : DefaultGenerationConfig() {
        override fun isGenerateBuilders(): Boolean { // set config option by overriding method
            return false
        }

        override fun getAnnotationStyle(): AnnotationStyle {
            return AnnotationStyle.NONE
        }

        override fun getCustomAnnotator(): Class<out Annotator> {
            return CustomAnnotator::class.java
        }

        override fun isIncludeGetters(): Boolean {
            return false
        }

        override fun isIncludeSetters(): Boolean {
            return false
        }

        override fun isIncludeHashcodeAndEquals(): Boolean {
            return false
        }

        override fun isIncludeToString(): Boolean {
            return false
        }

        override fun isRemoveOldOutput(): Boolean {
            return true
        }

        override fun getDateTimeType(): String {
            return "java.util.Date"
        }

        override fun getDateType(): String {
            return "java.util.Date"
        }

        override fun isIncludeAdditionalProperties(): Boolean {
            return false
        }

        override fun isUseTitleAsClassname(): Boolean {
            return false
        }

        override fun getPropertyWordDelimiters(): CharArray {
            return charArrayOf()
        }
    }

    val outputDir = File(OUTPUT_DIR)
    log.info("generating {} in {}", PACKAGE, outputDir.absolutePath)

    val mapper = SchemaMapper(RuleFactory(config, CustomAnnotator(), SchemaStore()), SchemaGenerator())
    mapper.generate(codeModel, "ClassName", PACKAGE, source)
    codeModel.build(outputDir)
}



package spu.ecp.json

import com.fasterxml.jackson.databind.JsonNode
import com.sun.codemodel.*
import org.codehaus.jackson.annotate.JsonCreator
import org.codehaus.jackson.annotate.JsonProperty
import org.codehaus.jackson.annotate.JsonValue
import org.jsonschema2pojo.AbstractAnnotator
import org.slf4j.Logger
import org.slf4j.LoggerFactory

class CustomAnnotator : AbstractAnnotator() {
    override fun propertyField(field: JFieldVar, clazz: JDefinedClass, propertyName: String, propertyNode: JsonNode) {
//        super.propertyField(field, clazz, propertyName, propertyNode);

        log.debug("processing class: {} field: {} ", clazz.name(), field.name())

        if (!isCollection(field.type())) {
            field.annotate(javax.annotation.Nullable::class.java)
        }

        field.annotate(JsonProperty::class.java).param("value", propertyName)

        (field.type() as? JDefinedClass)?.methods()?.forEach { method ->
            when (method.name()) {
                "toString" -> if (method.annotations().size < 2) {
                    method.annotate(JsonValue::class.java)
                }
                "fromValue" -> if (method.annotations().isEmpty()) {
                    method.annotate(JsonCreator::class.java)
                }
            }
        }

    }

    fun isCollection(jType: JType): Boolean {
        val typeName: String = jType.toString()
        return typeName.contains("List") ||
                typeName.contains("Set") ||
                typeName.contains("Map") ||
                typeName.contains("Collection")
    }

    companion object {
        private val log: Logger = LoggerFactory.getLogger(CustomAnnotator::class.java)
    }
}


plugins {
    id 'org.jetbrains.kotlin.jvm' version '2.0.0'
}

group = 'spu.ecp.json'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.jsonschema2pojo:jsonschema2pojo-core:1.2.2'
    implementation 'org.codehaus.jackson:jackson-core-asl:1.9.13'
    implementation 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
    implementation 'org.slf4j:slf4j-log4j12:1.7.30'
    testImplementation 'org.jetbrains.kotlin:kotlin-test'
}

test {
    useJUnitPlatform()
}
kotlin {
    jvmToolchain(8)
}

tasks.register('runMain', JavaExec) {
    main = 'spu.ecp.json.MainKt'
    classpath = sourceSets.main.runtimeClasspath
}


shme7ev avatar Oct 09 '24 07:10 shme7ev

@joelittlejohn I believe it's related to this change https://github.com/joelittlejohn/jsonschema2pojo/commit/59209437761369030c7d8cbb229bc73ce9d698d4#diff-f97fbc485092d3203a6f40624b3e8b7ba87e1301365e67be8f12c0319a06a030L24-R24

- import org.apache.commons.lang3.text.WordUtils;
+ import org.apache.commons.text.WordUtils;

The org.apache.commons.lang3.text.WordUtils has following implementation:

    public static String capitalizeFully(String str, final char... delimiters) {
        final int delimLen = delimiters == null ? -1 : delimiters.length;
        if (StringUtils.isEmpty(str) || delimLen == 0) {
            return str;
        }
        str = str.toLowerCase();
        return capitalize(str, delimiters);
    }

whereas org.apache.commons.text.WordUtils following:

    public static String capitalizeFully(String str, final char... delimiters) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        str = str.toLowerCase();
        return capitalize(str, delimiters);
    }

Note how latter is no longer doing bailout when delimiters length is 0 (which it is in given case as per configuration provided by @shme7ev).

I would leave the decision as to fix this on jsonschema2pojo side, report a bug to commons-text/commons-lang maintainers and wait for them to fix or leave it as it is up to you.

Update: it looks like given behavioral change was a deliberate decision taken by commons-text maintainers (see TEXT-88).

unkish avatar Nov 10 '24 19:11 unkish