Skip to content

[BUG] [Kotlin] Missing string array check for validity #21284

@andreas-umbricht

Description

@andreas-umbricht
openapi-generator version

7.13.0

OpenAPI declaration file content or url
"data": {
  "type": "array",
  "items": {
    "oneOf": [
      {
        "$ref": "#/components/schemas/LocationDataTemperature"
      },
      {
        "$ref": "#/components/schemas/LocationDataWind"
      }
    ]
  }
},

"LocationDataTemperature": {
  "type": "object",
  "properties": {
    "display": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": ["display"]
},

"LocationDataWind": {
  "type": "object",
  "properties": {
    "display": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "FF_KMH": {
            "type": "number",
            "format": "float"
          },
          "FX_KMH": {
            "type": "number",
            "format": "float"
          },
          "DD_DEG": {
            "type": "integer"
          }
        },
        "required": ["FF_KMH", "FX_KMH", "DD_DEG"]
      }
    }
  },
  "required": ["display"]
Generation Details

Gradle Task

    val meteoMapApiName = "MeteoMap"
    val meteoMapApiGenPackage = "${project.android.namespace}.${meteoMapApiName.lowercase()}"
    val generateMeteoMap by registering(GenerateTask::class) {
        generatorName.set("kotlin")
        inputSpec.set("${project.projectDir}/${meteoMapApiName.lowercase()}-api.json")
        outputDir.set("$buildDir/generated")
        packageName.set(meteoMapApiGenPackage)
        apiPackage.set("$meteoMapApiGenPackage.api")
        modelNamePrefix.set(meteoMapApiName)
        invokerPackage.set("$meteoMapApiGenPackage.invoker")
        modelPackage.set("$meteoMapApiGenPackage.model")
        configOptions.set(apiConfigOptions)
        additionalProperties.set(
            mapOf(
                "enumPropertyNaming" to "UPPERCASE",
                "useCoroutines" to "true",
                "moshiCodeGen" to "true",
                "generateOneOfAnyOfWrappers" to "true",
                "serializationLibrary" to "gson",
            ),
        )
        library.set("jvm-retrofit2")
    }
Steps to reproduce

When we receive a response from the server and parse it's body, an exception is thrown. Because too many Type Adapters match with the result.

Example of a json response:

"display": [
  {
  "FF_KMH": 4,
  "FX_KMH": 7,
  "DD_DEG": 212,
  "dateTime": "2025-05-15T00:00:00+00:00"
  }
]

This should obviousley only match to the type of LocationDataWind according to the schema provided above, but also the type of LocationDataTemperature apparantly does match the result. So I checked out the LocationDataTemperature.validateJsonElement() function.

        @Throws(IOException::class)
        fun validateJsonElement(jsonElement: JsonElement?) {
            if (jsonElement == null) {
              require(openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null
                String.format("The required field(s) %s in MeteoMapLocationDataTemperature is not found in the empty JSON string", MeteoMapLocationDataTemperature.openapiRequiredFields.toString())
              }
            }

            // check to make sure all required properties/fields are present in the JSON string
            for (requiredField in openapiRequiredFields) {
              requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) {
                String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonElement.toString())
              }
            }
            val jsonObj = jsonElement!!.getAsJsonObject()
            // ensure the required json array is present
            requireNotNull(jsonObj["display"]) {
              "Expected the field `display` to be an array in the JSON string but got `null`"
            }
            require(jsonObj["display"].isJsonArray()) {
              String.format("Expected the field `display` to be an array in the JSON string but got `%s`", jsonObj["display"].toString())
            }
        }

It looks like the validate function here only checks if an array is present and then declares the json as valid. It does not check if the items inside the array are actual strings. This check is just simply missing. In the LocationDataWind.validateJsonElement() function there is such a check implemented:

for (i in 0 until jsonObj.getAsJsonArray("display").size()) {
  MeteoMapLocationDataWindDisplayInner.validateJsonElement(jsonObj.getAsJsonArray("display").get(i))
} 
Suggest a fix

If there is a list of strings, the validateJsonElement() should also contain a check that all elements inside the array are in fact strings. This prevents false matches in a oneOf schema declaration.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions