Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.2.2 capitalized property names replaced with lowercase #1637

Open
shme7ev opened this issue Oct 8, 2024 · 3 comments
Open

1.2.2 capitalized property names replaced with lowercase #1637

shme7ev opened this issue Oct 8, 2024 · 3 comments

Comments

@shme7ev
Copy link

shme7ev commented Oct 8, 2024

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;
@joelittlejohn
Copy link
Owner

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?

@shme7ev
Copy link
Author

shme7ev commented Oct 9, 2024

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
}


@unkish
Copy link
Collaborator

unkish commented Nov 10, 2024

@joelittlejohn I believe it's related to this change 5920943#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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants