From 4b0df27fe7d23b441ffd1b3bc29bf34bb5306673 Mon Sep 17 00:00:00 2001 From: "Arkady.Bazhanov" Date: Sun, 14 Jul 2024 15:20:28 +0200 Subject: [PATCH] Fix #429: Support nullable array elements Also fix nullable property detection --- .../builder/KotlinListClassCodeBuilder.kt | 6 +- .../classscodestruct/GenericListClass.kt | 4 +- .../model/classscodestruct/ListClass.kt | 1 + .../DataClassGeneratorByJSONSchema.kt | 7 ++- .../GenericListClassGeneratorByJSONArray.kt | 32 +++++++--- .../ListClassGeneratorByJSONArray.kt | 15 ++--- .../DataClassGeneratorByJSONObjectTest.kt | 4 +- .../seal/jsontokotlin/DataClassMakerTest.kt | 4 +- .../jsontokotlin/JsonSchemaGeneratorTest.kt | 58 +++++++++---------- .../ListClassGeneratorByJSONArrayTest.kt | 22 ++++--- .../classscodestruct/GenericListClassTest.kt | 2 +- .../model/classscodestruct/ListClassTest.kt | 16 ++--- .../jsontokotlin/regression/Issue429Test.kt | 31 ++++++++++ 13 files changed, 130 insertions(+), 72 deletions(-) create mode 100644 src/test/kotlin/wu/seal/jsontokotlin/regression/Issue429Test.kt diff --git a/src/main/kotlin/wu/seal/jsontokotlin/model/builder/KotlinListClassCodeBuilder.kt b/src/main/kotlin/wu/seal/jsontokotlin/model/builder/KotlinListClassCodeBuilder.kt index 6dca0c92..2438b383 100644 --- a/src/main/kotlin/wu/seal/jsontokotlin/model/builder/KotlinListClassCodeBuilder.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/model/builder/KotlinListClassCodeBuilder.kt @@ -12,11 +12,12 @@ class KotlinListClassCodeBuilder : ICodeBuilder { override fun getCode(clazz: ListClass): String { clazz.run { + val elementType = if (nullableElements) generic.name + "?" else generic.name return if (generic.modifiable.not()) { getOnlyCurrentCode() } else { """ - class $name : ArrayList<${generic.name}>(){ + class $name : ArrayList<$elementType>(){ ${referencedClasses.filter { it.modifiable }.joinToString("\n\n") { it.getCode().prependIndent(" $indent") }} } """.trimIndent() @@ -26,8 +27,9 @@ ${referencedClasses.filter { it.modifiable }.joinToString("\n\n") { it.getCode() override fun getOnlyCurrentCode(clazz: ListClass): String { clazz.run { + val elementType = if (nullableElements) generic.name + "?" else generic.name return """ - class $name : ArrayList<${generic.name}>() + class $name : ArrayList<$elementType>() """.trimIndent() } } diff --git a/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClass.kt b/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClass.kt index 54f3315d..7390dd72 100644 --- a/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClass.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClass.kt @@ -7,9 +7,9 @@ import wu.seal.jsontokotlin.utils.LogUtil * Created by Seal.Wu on 2019-11-23 * present a list class type like List */ -data class GenericListClass(override val generic: KotlinClass) : UnModifiableGenericClass() { +data class GenericListClass(override val generic: KotlinClass, val nullableElements: Boolean) : UnModifiableGenericClass() { override val name: String - get() = "List<${generic.name}>" + get() = if (nullableElements) "List<${generic.name}?>" else "List<${generic.name}>" override val hasGeneric: Boolean = true diff --git a/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClass.kt b/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClass.kt index 325e8d75..8326815e 100644 --- a/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClass.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClass.kt @@ -10,6 +10,7 @@ import wu.seal.jsontokotlin.model.builder.* data class ListClass( override val name: String, override val generic: KotlinClass, + val nullableElements: Boolean, override val referencedClasses: List = listOf(generic), override val modifiable: Boolean = true, override val codeBuilder: KotlinListClassCodeBuilder = KotlinListClassCodeBuilder.DEFAULT diff --git a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/DataClassGeneratorByJSONSchema.kt b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/DataClassGeneratorByJSONSchema.kt index 2263da81..c30d2bc1 100755 --- a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/DataClassGeneratorByJSONSchema.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/DataClassGeneratorByJSONSchema.kt @@ -58,7 +58,7 @@ class DataClassGeneratorByJSONSchema(private val rootClassName: String, private ): Property { val (jsonClassName, realDef) = getRealDefinition(jsonProp) val typeClass = resolveTypeClass(realDef.typeString, jsonClassName, realDef, propertyName) - val value = if (isRequired || !jsonProp.isTypeNullable) getDefaultValue(typeClass.name) else null + val value = if (isRequired || !realDef.isTypeNullable) getDefaultValue(typeClass.name) else null return Property( originName = propertyName, originJsonValue = value, @@ -86,7 +86,10 @@ class DataClassGeneratorByJSONSchema(private val rootClassName: String, private propertyName, false ) - GenericListClass(generic = innerProperty.typeObject) + GenericListClass( + generic = innerProperty.typeObject, + nullableElements = innerProperty.originJsonValue == null, + ) } checkEnum && realDef.enum != null -> resolveEnumClass(realDef, simpleName) checkSealed && realDef.anyOf != null -> resolveSealedClass(realDef, simpleName) diff --git a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/GenericListClassGeneratorByJSONArray.kt b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/GenericListClassGeneratorByJSONArray.kt index cda9baf7..c0c9526e 100644 --- a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/GenericListClassGeneratorByJSONArray.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/GenericListClassGeneratorByJSONArray.kt @@ -2,8 +2,6 @@ package wu.seal.jsontokotlin.utils.classgenerator import com.google.gson.Gson import com.google.gson.JsonArray -import com.google.gson.JsonNull -import com.google.gson.JsonObject import wu.seal.jsontokotlin.model.classscodestruct.GenericListClass import wu.seal.jsontokotlin.model.classscodestruct.KotlinClass import wu.seal.jsontokotlin.utils.* @@ -15,8 +13,9 @@ import wu.seal.jsontokotlin.utils.* class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArrayString: String) { private val tag = "ListClassGeneratorByJSONArray" - private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java) + private val jsonArray: JsonArray = readJsonArray(jsonArrayString) private val jsonArrayExcludeNull = jsonArray.filterOutNullElement() + private val hasNulls get() = jsonArray.size() != jsonArrayExcludeNull.size() fun generate(): GenericListClass { @@ -24,11 +23,11 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra when { jsonArray.size() == 0 -> { LogUtil.i("$tag jsonArray size is 0, return GenericListClass with generic type ANY") - return GenericListClass(generic = KotlinClass.ANY) + return GenericListClass(generic = KotlinClass.ANY, nullableElements = true) } jsonArray.allItemAreNullElement() -> { LogUtil.i("$tag jsonArray allItemAreNullElement, return GenericListClass with generic type ${KotlinClass.ANY.name}") - return GenericListClass(generic = KotlinClass.ANY) + return GenericListClass(generic = KotlinClass.ANY, nullableElements = true) } jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> { // if all elements are numbers, we need to select the larger scope of Kotlin types among the elements @@ -37,24 +36,24 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra val p = jsonArrayExcludeNull[0].asJsonPrimitive; val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArrayExcludeNull) else p.toKotlinClass() LogUtil.i("$tag jsonArray allElementAreSamePrimitiveType, return GenericListClass with generic type ${elementKotlinClass.name}") - return GenericListClass(generic = elementKotlinClass) + return GenericListClass(generic = elementKotlinClass, nullableElements = hasNulls) } jsonArrayExcludeNull.allItemAreObjectElement() -> { val fatJsonObject = jsonArrayExcludeNull.getFatJsonObject() val itemObjClassName = getRecommendItemName(jsonKey) val dataClassFromJsonObj = DataClassGeneratorByJSONObject(itemObjClassName, fatJsonObject).generate() LogUtil.i("$tag jsonArray allItemAreObjectElement, return GenericListClass with generic type ${dataClassFromJsonObj.name}") - return GenericListClass(generic = dataClassFromJsonObj) + return GenericListClass(generic = dataClassFromJsonObj, nullableElements = hasNulls) } jsonArrayExcludeNull.allItemAreArrayElement() -> { val fatJsonArray = jsonArrayExcludeNull.getFatJsonArray() val genericListClassFromFatJsonArray = GenericListClassGeneratorByJSONArray(jsonKey, fatJsonArray.toString()).generate() LogUtil.i("$tag jsonArray allItemAreArrayElement, return GenericListClass with generic type ${genericListClassFromFatJsonArray.name}") - return GenericListClass(generic = genericListClassFromFatJsonArray) + return GenericListClass(generic = genericListClassFromFatJsonArray, nullableElements = hasNulls) } else -> { LogUtil.i("$tag jsonArray exception shouldn't come here, return GenericListClass with generic type ANY") - return GenericListClass(generic = KotlinClass.ANY) + return GenericListClass(generic = KotlinClass.ANY, nullableElements = true) } } } @@ -63,3 +62,18 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra return adjustPropertyNameForGettingArrayChildType(jsonKey) } } + +internal fun readJsonArray(jsonArrayString: String): JsonArray { + val jsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java) + // if the last element is null, it may actually be a trailing comma + if (jsonArray.size() == 0 || !jsonArray.last().isJsonNull) { + return jsonArray + } + // check if the json array has a trailing comma + if (jsonArrayString.trimEnd().removeSuffix("]").trimEnd().endsWith(",")) { + val lastComma = jsonArrayString.lastIndexOf(",") + return Gson().fromJson(jsonArrayString.removeRange(lastComma..lastComma), JsonArray::class.java) + } else { + return jsonArray + } +} diff --git a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/ListClassGeneratorByJSONArray.kt b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/ListClassGeneratorByJSONArray.kt index 487310f3..ce6acc9d 100644 --- a/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/ListClassGeneratorByJSONArray.kt +++ b/src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/ListClassGeneratorByJSONArray.kt @@ -12,19 +12,20 @@ import wu.seal.jsontokotlin.utils.* class ListClassGeneratorByJSONArray(private val className: String, jsonArrayString: String) { private val tag = "ListClassGeneratorByJSONArray" - private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java) + private val jsonArray: JsonArray = readJsonArray(jsonArrayString) private val jsonArrayExcludeNull = jsonArray.filterOutNullElement() + private val hasNulls get() = jsonArray.size() != jsonArrayExcludeNull.size() fun generate(): ListClass { when { jsonArray.size() == 0 -> { LogUtil.i("$tag jsonArray size is 0, return ListClass with generic type ANY") - return ListClass(name = className, generic = KotlinClass.ANY) + return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true) } jsonArray.allItemAreNullElement() -> { LogUtil.i("$tag jsonArray allItemAreNullElement, return ListClass with generic type ${KotlinClass.ANY.name}") - return ListClass(name = className, generic = KotlinClass.ANY) + return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true) } jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> { @@ -34,25 +35,25 @@ class ListClassGeneratorByJSONArray(private val className: String, jsonArrayStri val p = jsonArrayExcludeNull[0].asJsonPrimitive; val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArrayExcludeNull) else p.toKotlinClass() LogUtil.i("$tag jsonArray allElementAreSamePrimitiveType, return ListClass with generic type ${elementKotlinClass.name}") - return ListClass(name = className, generic = elementKotlinClass) + return ListClass(name = className, generic = elementKotlinClass, nullableElements = hasNulls) } jsonArrayExcludeNull.allItemAreObjectElement() -> { val fatJsonObject = jsonArrayExcludeNull.getFatJsonObject() val itemObjClassName = "${className}Item" val dataClassFromJsonObj = DataClassGeneratorByJSONObject(itemObjClassName, fatJsonObject).generate() LogUtil.i("$tag jsonArray allItemAreObjectElement, return ListClass with generic type ${dataClassFromJsonObj.name}") - return ListClass(className, dataClassFromJsonObj) + return ListClass(className, dataClassFromJsonObj, nullableElements = hasNulls) } jsonArrayExcludeNull.allItemAreArrayElement() -> { val fatJsonArray = jsonArrayExcludeNull.getFatJsonArray() val itemArrayClassName = "${className}SubList" val listClassFromFatJsonArray = ListClassGeneratorByJSONArray(itemArrayClassName, fatJsonArray.toString()).generate() LogUtil.i("$tag jsonArray allItemAreArrayElement, return ListClass with generic type ${listClassFromFatJsonArray.name}") - return ListClass(className, listClassFromFatJsonArray) + return ListClass(className, listClassFromFatJsonArray, nullableElements = hasNulls) } else -> { LogUtil.i("$tag jsonArray exception shouldn't come here, return ListClass with generic type ANY") - return ListClass(name = className, generic = KotlinClass.ANY) + return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true) } } } diff --git a/src/test/kotlin/wu/seal/jsontokotlin/DataClassGeneratorByJSONObjectTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/DataClassGeneratorByJSONObjectTest.kt index 546bc73e..114574f0 100755 --- a/src/test/kotlin/wu/seal/jsontokotlin/DataClassGeneratorByJSONObjectTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/DataClassGeneratorByJSONObjectTest.kt @@ -147,8 +147,8 @@ class DataClassGeneratorByJSONObjectTest { val glossDefP2 = glossDefDataClass.properties[1] glossDefP2.name.should.be.equal("GlossSeeAlso") - glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING).name) + glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false).name) glossDefP2.originJsonValue.should.be.empty - glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING)) + glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false)) } } \ No newline at end of file diff --git a/src/test/kotlin/wu/seal/jsontokotlin/DataClassMakerTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/DataClassMakerTest.kt index a99ad27c..c39f5777 100755 --- a/src/test/kotlin/wu/seal/jsontokotlin/DataClassMakerTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/DataClassMakerTest.kt @@ -147,9 +147,9 @@ class DataClassMakerTest { val glossDefP2 = glossDefDataClass.properties[1] glossDefP2.name.should.be.equal("GlossSeeAlso") - glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING).name) + glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false).name) glossDefP2.originJsonValue.should.be.empty - glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING)) + glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false)) } diff --git a/src/test/kotlin/wu/seal/jsontokotlin/JsonSchemaGeneratorTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/JsonSchemaGeneratorTest.kt index d95f7157..a0bc6419 100644 --- a/src/test/kotlin/wu/seal/jsontokotlin/JsonSchemaGeneratorTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/JsonSchemaGeneratorTest.kt @@ -328,7 +328,7 @@ data class veggie( /** * Тип события */ - val typeA: LogEventType?, + val typeA: LogEventType, /** * Подсобытие (набор значений зависит от действия) */ @@ -1012,107 +1012,107 @@ enum class LogEventType(val value: Int) { /** * Gets or sets the IReferenceResolver used by the serializer when resolving references. */ - val ReferenceResolver: IReferenceResolver?, + val ReferenceResolver: IReferenceResolver, /** * Gets or sets the SerializationBinder used by the serializer when resolving type names. */ - val Binder: SerializationBinder?, + val Binder: SerializationBinder, /** * Gets or sets the ISerializationBinder used by the serializer when resolving type names. */ - val SerializationBinder: ISerializationBinder?, + val SerializationBinder: ISerializationBinder, /** * Gets or sets the ITraceWriter used by the serializer when writing trace messages. */ - val TraceWriter: ITraceWriter?, + val TraceWriter: ITraceWriter, /** * Gets or sets the equality comparer used by the serializer when comparing references. */ - val EqualityComparer: IEqualityComparer?, + val EqualityComparer: IEqualityComparer, /** * Gets or sets how type name writing and reading is handled by the serializer.The default value is None. */ - val TypeNameHandling: TypeNameHandling?, + val TypeNameHandling: TypeNameHandling, /** * Gets or sets how a type name assembly is written and resolved by the serializer.The default value is Simple. */ - val TypeNameAssemblyFormat: FormatterAssemblyStyle?, + val TypeNameAssemblyFormat: FormatterAssemblyStyle, /** * Gets or sets how a type name assembly is written and resolved by the serializer.The default value is Simple. */ - val TypeNameAssemblyFormatHandling: TypeNameAssemblyFormatHandling?, + val TypeNameAssemblyFormatHandling: TypeNameAssemblyFormatHandling, /** * Gets or sets how object references are preserved by the serializer.The default value is None. */ - val PreserveReferencesHandling: PreserveReferencesHandling?, + val PreserveReferencesHandling: PreserveReferencesHandling, /** * Gets or sets how reference loops (e.g. a class referencing itself) is handled.The default value is Error. */ - val ReferenceLoopHandling: ReferenceLoopHandling?, + val ReferenceLoopHandling: ReferenceLoopHandling, /** * Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization.The default value is Ignore. */ - val MissingMemberHandling: MissingMemberHandling?, + val MissingMemberHandling: MissingMemberHandling, /** * Gets or sets how null values are handled during serialization and deserialization.The default value is Include. */ - val NullValueHandling: NullValueHandling?, + val NullValueHandling: NullValueHandling, /** * Gets or sets how default values are handled during serialization and deserialization.The default value is Include. */ - val DefaultValueHandling: DefaultValueHandling?, + val DefaultValueHandling: DefaultValueHandling, /** * Gets or sets how objects are created during deserialization.The default value is Auto. */ - val ObjectCreationHandling: ObjectCreationHandling?, + val ObjectCreationHandling: ObjectCreationHandling, /** * Gets or sets how constructors are used during deserialization.The default value is Default. */ - val ConstructorHandling: ConstructorHandling?, + val ConstructorHandling: ConstructorHandling, /** * Gets or sets how metadata properties are used during deserialization.The default value is Default. */ - val MetadataPropertyHandling: MetadataPropertyHandling?, + val MetadataPropertyHandling: MetadataPropertyHandling, /** * Gets a collection JsonConverter that will be used during serialization. */ - val Converters: List?, + val Converters: List, /** * Gets or sets the contract resolver used by the serializer whenserializing .NET objects to JSON and vice versa. */ - val ContractResolver: IContractResolver?, + val ContractResolver: IContractResolver, /** * Gets or sets the StreamingContext used by the serializer when invoking serialization callback methods. */ - val Context: StreamingContext?, + val Context: StreamingContext, /** * Indicates how JSON text output is formatted.The default value is None. */ - val Formatting: Formatting?, + val Formatting: Formatting, /** * Gets or sets how dates are written to JSON text.The default value is IsoDateFormat. */ - val DateFormatHandling: DateFormatHandling?, + val DateFormatHandling: DateFormatHandling, /** * Gets or sets how DateTime time zones are handled during serialization and deserialization.The default value is RoundtripKind. */ - val DateTimeZoneHandling: DateTimeZoneHandling?, + val DateTimeZoneHandling: DateTimeZoneHandling, /** * Gets or sets how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.The default value is DateTime. */ - val DateParseHandling: DateParseHandling?, + val DateParseHandling: DateParseHandling, /** * Gets or sets how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.The default value is Double. */ - val FloatParseHandling: FloatParseHandling?, + val FloatParseHandling: FloatParseHandling, /** * Gets or sets how special floating point numbers, e.g. NaN,PositiveInfinity and NegativeInfinity,are written as JSON text.The default value is String. */ - val FloatFormatHandling: FloatFormatHandling?, + val FloatFormatHandling: FloatFormatHandling, /** * Gets or sets how strings are escaped when writing JSON text.The default value is Default. */ - val StringEscapeHandling: StringEscapeHandling?, + val StringEscapeHandling: StringEscapeHandling, /** * Gets or sets how DateTime and DateTimeOffset values are formatted when writing JSON text,and the expected date format when reading JSON text.The default value is "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK". */ @@ -1150,7 +1150,7 @@ enum class LogEventType(val value: Int) { /** * Gets the TraceLevel that will be used to filter the trace messages passed to the writer.For example a filter level of Info will exclude Verbose messages and include Info,Warning and Error messages. */ - val LevelFilter: TraceLevel? + val LevelFilter: TraceLevel ) class IEqualityComparer @@ -1292,7 +1292,7 @@ enum class LogEventType(val value: Int) { class IContractResolver data class StreamingContext( - val State: StreamingContextStates?, + val State: StreamingContextStates, val Context: Any? ) diff --git a/src/test/kotlin/wu/seal/jsontokotlin/ListClassGeneratorByJSONArrayTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/ListClassGeneratorByJSONArrayTest.kt index b1aa8e8b..8d1bdbe8 100644 --- a/src/test/kotlin/wu/seal/jsontokotlin/ListClassGeneratorByJSONArrayTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/ListClassGeneratorByJSONArrayTest.kt @@ -22,17 +22,17 @@ class ListClassGeneratorByJSONArrayTest { @Test fun generateBaseListTypeTest() { ListClassGeneratorByJSONArray("TestList", "[]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.ANY)) + .should.be.equal(ListClass("TestList", KotlinClass.ANY, nullableElements = true)) ListClassGeneratorByJSONArray("TestList", "[1]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.INT)) + .should.be.equal(ListClass("TestList", KotlinClass.INT, nullableElements = false)) ListClassGeneratorByJSONArray("TestList", "[1.0]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.DOUBLE)) + .should.be.equal(ListClass("TestList", KotlinClass.DOUBLE, nullableElements = false)) ListClassGeneratorByJSONArray("TestList", "[true]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.BOOLEAN)) + .should.be.equal(ListClass("TestList", KotlinClass.BOOLEAN, nullableElements = false)) ListClassGeneratorByJSONArray("TestList", "[100000000000000]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.LONG)) + .should.be.equal(ListClass("TestList", KotlinClass.LONG, nullableElements = false)) ListClassGeneratorByJSONArray("TestList", "[null]").generate() - .should.be.equal(ListClass("TestList", KotlinClass.ANY)) + .should.be.equal(ListClass("TestList", KotlinClass.ANY, nullableElements = true)) } @Test @@ -40,12 +40,18 @@ class ListClassGeneratorByJSONArrayTest { val result = ListClassGeneratorByJSONArray("TestList", "[{p1:1}]").generate() val dataClassProperty = Property(name = "p1",originName = "p1",type = "Int",comment = "1",originJsonValue = "1",typeObject = KotlinClass.INT) val itemClass = DataClass(name = "TestListItem",properties = listOf(dataClassProperty)) - result.should.be.equal(ListClass("TestList", itemClass)) + result.should.be.equal(ListClass("TestList", itemClass, nullableElements = false)) } @Test fun generateListClassWithListClass() { val result = ListClassGeneratorByJSONArray("TestList", "[[]]").generate() - result.should.be.equal(ListClass("TestList", ListClass("TestListSubList", KotlinClass.ANY))) + result.should.be.equal( + ListClass( + "TestList", + ListClass("TestListSubList", KotlinClass.ANY, nullableElements = true), + nullableElements = false + ) + ) } } \ No newline at end of file diff --git a/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClassTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClassTest.kt index 854ad592..af2f7bee 100644 --- a/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClassTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClassTest.kt @@ -16,7 +16,7 @@ class GenericListClassTest { @Test fun replaceReferencedClasses() { - val genericListClass = GenericListClass(generic = KotlinClass.ANY) + val genericListClass = GenericListClass(generic = KotlinClass.ANY, nullableElements = false) val newGenericListClass = genericListClass.replaceReferencedClasses(mapOf(KotlinClass.BOOLEAN to KotlinClass.DOUBLE, KotlinClass.ANY to KotlinClass.BOOLEAN)) newGenericListClass.generic.should.be.equal(KotlinClass.BOOLEAN) } diff --git a/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClassTest.kt b/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClassTest.kt index 99457289..d8f78c95 100644 --- a/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClassTest.kt +++ b/src/test/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClassTest.kt @@ -15,13 +15,13 @@ class ListClassTest { @Test fun getOnlyCurrentCode() { - val expected = "class ListTest : ArrayList()" - val listClass = ListClass("ListTest", KotlinClass.ANY) + val expected = "class ListTest : ArrayList()" + val listClass = ListClass("ListTest", KotlinClass.ANY, nullableElements = true) listClass.getOnlyCurrentCode().should.equal(expected) val dataClassProperty = Property(name = "p1", originName = "p1", type = "String", typeObject = KotlinClass.STRING) val generic = DataClass(name = "Data", properties = listOf(dataClassProperty)) - val listClass2 = ListClass("ListTest", generic) + val listClass2 = ListClass("ListTest", generic, nullableElements = false) val expected2 = """ class ListTest : ArrayList() """.trimIndent() @@ -30,7 +30,7 @@ class ListClassTest { @Test fun replaceReferencedClasses() { - var listClass = ListClass("ListTest", KotlinClass.ANY) + var listClass = ListClass("ListTest", KotlinClass.ANY, nullableElements = false) listClass = listClass.replaceReferencedClasses(mapOf(KotlinClass.ANY to KotlinClass.DOUBLE)) listClass.generic.should.be.equal(KotlinClass.DOUBLE) listClass.referencedClasses.size.should.be.equal(1) @@ -39,7 +39,7 @@ class ListClassTest { @Test fun rename() { - val listClass = ListClass("ListTest", KotlinClass.ANY) + val listClass = ListClass("ListTest", KotlinClass.ANY, nullableElements = false) val listClassNew = listClass.rename("ListTestNew") listClassNew.name.should.be.equal("ListTestNew") } @@ -48,7 +48,7 @@ class ListClassTest { fun getCode() { val dataClassProperty = Property(name = "p1", originName = "p1", type = "String", typeObject = KotlinClass.STRING) val generic = DataClass(name = "Data", properties = listOf(dataClassProperty)) - val listClass = ListClass("ListTest", generic) + val listClass = ListClass("ListTest", generic, nullableElements = false) val expected = """ class ListTest : ArrayList(){ data class Data( @@ -62,13 +62,13 @@ class ListClassTest { @Test fun getReferencedClasses() { - val listClass = ListClass("ListTest", KotlinClass.ANY) + val listClass = ListClass("ListTest", KotlinClass.ANY, nullableElements = false) listClass.referencedClasses.size.should.be.equal(1) listClass.referencedClasses[0].should.be.equal(KotlinClass.ANY) val dataClassProperty = Property(name = "p1", originName = "p1", type = "String", typeObject = KotlinClass.STRING) val generic = DataClass(name = "Data", properties = listOf(dataClassProperty)) - val listClass2 = ListClass("ListTest", generic) + val listClass2 = ListClass("ListTest", generic, nullableElements = false) listClass2.referencedClasses.size.should.be.equal(1) listClass2.referencedClasses[0].should.be.equal(generic) } diff --git a/src/test/kotlin/wu/seal/jsontokotlin/regression/Issue429Test.kt b/src/test/kotlin/wu/seal/jsontokotlin/regression/Issue429Test.kt new file mode 100644 index 00000000..34dd84ff --- /dev/null +++ b/src/test/kotlin/wu/seal/jsontokotlin/regression/Issue429Test.kt @@ -0,0 +1,31 @@ +package wu.seal.jsontokotlin.regression + +import com.winterbe.expekt.should +import org.junit.Test +import wu.seal.jsontokotlin.generateKotlinClassCode +import wu.seal.jsontokotlin.model.DefaultValueStrategy +import wu.seal.jsontokotlin.model.TargetJsonConverter +import wu.seal.jsontokotlin.test.TestConfig +import wu.seal.jsontokotlin.utils.BaseTest + +class Issue429Test : BaseTest() { + @Test + fun testIssue429() { + TestConfig.apply { + isCommentOff = true + targetJsonConvertLib = TargetJsonConverter.None + defaultValueStrategy = DefaultValueStrategy.None + isNestedClassModel = false + } + val json1 = """["hi", null, "hi"]""" + val expected1 = "class Test : ArrayList()" + json1.generateKotlinClassCode("Test").should.equal(expected1) + val json2 = """[{}, null, {}]""" + val expected2 = """ + class Test : ArrayList() + + class TestItem + """.trimIndent() + json2.generateKotlinClassCode("Test").should.equal(expected2) + } +} \ No newline at end of file