Skip to content

Commit 65c7a4c

Browse files
authored
Merge pull request #430 from Kotlin/issue-429-fix
2 parents 1079dd4 + 447b913 commit 65c7a4c

13 files changed

+130
-72
lines changed

src/main/kotlin/wu/seal/jsontokotlin/model/builder/KotlinListClassCodeBuilder.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ class KotlinListClassCodeBuilder : ICodeBuilder<ListClass> {
1212

1313
override fun getCode(clazz: ListClass): String {
1414
clazz.run {
15+
val elementType = if (nullableElements) generic.name + "?" else generic.name
1516
return if (generic.modifiable.not()) {
1617
getOnlyCurrentCode()
1718
} else {
1819
"""
19-
class $name : ArrayList<${generic.name}>(){
20+
class $name : ArrayList<$elementType>(){
2021
${referencedClasses.filter { it.modifiable }.joinToString("\n\n") { it.getCode().prependIndent(" $indent") }}
2122
}
2223
""".trimIndent()
@@ -26,8 +27,9 @@ ${referencedClasses.filter { it.modifiable }.joinToString("\n\n") { it.getCode()
2627

2728
override fun getOnlyCurrentCode(clazz: ListClass): String {
2829
clazz.run {
30+
val elementType = if (nullableElements) generic.name + "?" else generic.name
2931
return """
30-
class $name : ArrayList<${generic.name}>()
32+
class $name : ArrayList<$elementType>()
3133
""".trimIndent()
3234
}
3335
}

src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/GenericListClass.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import wu.seal.jsontokotlin.utils.LogUtil
77
* Created by Seal.Wu on 2019-11-23
88
* present a list class type like List<Any>
99
*/
10-
data class GenericListClass(override val generic: KotlinClass) : UnModifiableGenericClass() {
10+
data class GenericListClass(override val generic: KotlinClass, val nullableElements: Boolean) : UnModifiableGenericClass() {
1111
override val name: String
12-
get() = "List<${generic.name}>"
12+
get() = if (nullableElements) "List<${generic.name}?>" else "List<${generic.name}>"
1313

1414
override val hasGeneric: Boolean = true
1515

src/main/kotlin/wu/seal/jsontokotlin/model/classscodestruct/ListClass.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import wu.seal.jsontokotlin.model.builder.*
1010
data class ListClass(
1111
override val name: String,
1212
override val generic: KotlinClass,
13+
val nullableElements: Boolean,
1314
override val referencedClasses: List<KotlinClass> = listOf(generic),
1415
override val modifiable: Boolean = true,
1516
override val codeBuilder: KotlinListClassCodeBuilder = KotlinListClassCodeBuilder.DEFAULT

src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/DataClassGeneratorByJSONSchema.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class DataClassGeneratorByJSONSchema(private val rootClassName: String, private
5858
): Property {
5959
val (jsonClassName, realDef) = getRealDefinition(jsonProp)
6060
val typeClass = resolveTypeClass(realDef.typeString, jsonClassName, realDef, propertyName)
61-
val value = if (isRequired || !jsonProp.isTypeNullable) getDefaultValue(typeClass.name) else null
61+
val value = if (isRequired || !realDef.isTypeNullable) getDefaultValue(typeClass.name) else null
6262
return Property(
6363
originName = propertyName,
6464
originJsonValue = value,
@@ -86,7 +86,10 @@ class DataClassGeneratorByJSONSchema(private val rootClassName: String, private
8686
propertyName,
8787
false
8888
)
89-
GenericListClass(generic = innerProperty.typeObject)
89+
GenericListClass(
90+
generic = innerProperty.typeObject,
91+
nullableElements = innerProperty.originJsonValue == null,
92+
)
9093
}
9194
checkEnum && realDef.enum != null -> resolveEnumClass(realDef, simpleName)
9295
checkSealed && realDef.anyOf != null -> resolveSealedClass(realDef, simpleName)

src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/GenericListClassGeneratorByJSONArray.kt

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package wu.seal.jsontokotlin.utils.classgenerator
22

33
import com.google.gson.Gson
44
import com.google.gson.JsonArray
5-
import com.google.gson.JsonNull
6-
import com.google.gson.JsonObject
75
import wu.seal.jsontokotlin.model.classscodestruct.GenericListClass
86
import wu.seal.jsontokotlin.model.classscodestruct.KotlinClass
97
import wu.seal.jsontokotlin.utils.*
@@ -15,20 +13,21 @@ import wu.seal.jsontokotlin.utils.*
1513
class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArrayString: String) {
1614

1715
private val tag = "ListClassGeneratorByJSONArray"
18-
private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java)
16+
private val jsonArray: JsonArray = readJsonArray(jsonArrayString)
1917
private val jsonArrayExcludeNull = jsonArray.filterOutNullElement()
18+
private val hasNulls get() = jsonArray.size() != jsonArrayExcludeNull.size()
2019

2120

2221
fun generate(): GenericListClass {
2322

2423
when {
2524
jsonArray.size() == 0 -> {
2625
LogUtil.i("$tag jsonArray size is 0, return GenericListClass with generic type ANY")
27-
return GenericListClass(generic = KotlinClass.ANY)
26+
return GenericListClass(generic = KotlinClass.ANY, nullableElements = true)
2827
}
2928
jsonArray.allItemAreNullElement() -> {
3029
LogUtil.i("$tag jsonArray allItemAreNullElement, return GenericListClass with generic type ${KotlinClass.ANY.name}")
31-
return GenericListClass(generic = KotlinClass.ANY)
30+
return GenericListClass(generic = KotlinClass.ANY, nullableElements = true)
3231
}
3332
jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> {
3433
// 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
3736
val p = jsonArrayExcludeNull[0].asJsonPrimitive;
3837
val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArrayExcludeNull) else p.toKotlinClass()
3938
LogUtil.i("$tag jsonArray allElementAreSamePrimitiveType, return GenericListClass with generic type ${elementKotlinClass.name}")
40-
return GenericListClass(generic = elementKotlinClass)
39+
return GenericListClass(generic = elementKotlinClass, nullableElements = hasNulls)
4140
}
4241
jsonArrayExcludeNull.allItemAreObjectElement() -> {
4342
val fatJsonObject = jsonArrayExcludeNull.getFatJsonObject()
4443
val itemObjClassName = getRecommendItemName(jsonKey)
4544
val dataClassFromJsonObj = DataClassGeneratorByJSONObject(itemObjClassName, fatJsonObject).generate()
4645
LogUtil.i("$tag jsonArray allItemAreObjectElement, return GenericListClass with generic type ${dataClassFromJsonObj.name}")
47-
return GenericListClass(generic = dataClassFromJsonObj)
46+
return GenericListClass(generic = dataClassFromJsonObj, nullableElements = hasNulls)
4847
}
4948
jsonArrayExcludeNull.allItemAreArrayElement() -> {
5049
val fatJsonArray = jsonArrayExcludeNull.getFatJsonArray()
5150
val genericListClassFromFatJsonArray = GenericListClassGeneratorByJSONArray(jsonKey, fatJsonArray.toString()).generate()
5251
LogUtil.i("$tag jsonArray allItemAreArrayElement, return GenericListClass with generic type ${genericListClassFromFatJsonArray.name}")
53-
return GenericListClass(generic = genericListClassFromFatJsonArray)
52+
return GenericListClass(generic = genericListClassFromFatJsonArray, nullableElements = hasNulls)
5453
}
5554
else -> {
5655
LogUtil.i("$tag jsonArray exception shouldn't come here, return GenericListClass with generic type ANY")
57-
return GenericListClass(generic = KotlinClass.ANY)
56+
return GenericListClass(generic = KotlinClass.ANY, nullableElements = true)
5857
}
5958
}
6059
}
@@ -63,3 +62,18 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra
6362
return adjustPropertyNameForGettingArrayChildType(jsonKey)
6463
}
6564
}
65+
66+
internal fun readJsonArray(jsonArrayString: String): JsonArray {
67+
val jsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java)
68+
// if the last element is null, it may actually be a trailing comma
69+
if (jsonArray.size() == 0 || !jsonArray.last().isJsonNull) {
70+
return jsonArray
71+
}
72+
// check if the json array has a trailing comma
73+
if (jsonArrayString.trimEnd().removeSuffix("]").trimEnd().endsWith(",")) {
74+
val lastComma = jsonArrayString.lastIndexOf(",")
75+
return Gson().fromJson(jsonArrayString.removeRange(lastComma..lastComma), JsonArray::class.java)
76+
} else {
77+
return jsonArray
78+
}
79+
}

src/main/kotlin/wu/seal/jsontokotlin/utils/classgenerator/ListClassGeneratorByJSONArray.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,20 @@ import wu.seal.jsontokotlin.utils.*
1212
class ListClassGeneratorByJSONArray(private val className: String, jsonArrayString: String) {
1313

1414
private val tag = "ListClassGeneratorByJSONArray"
15-
private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java)
15+
private val jsonArray: JsonArray = readJsonArray(jsonArrayString)
1616
private val jsonArrayExcludeNull = jsonArray.filterOutNullElement()
17+
private val hasNulls get() = jsonArray.size() != jsonArrayExcludeNull.size()
1718

1819
fun generate(): ListClass {
1920

2021
when {
2122
jsonArray.size() == 0 -> {
2223
LogUtil.i("$tag jsonArray size is 0, return ListClass with generic type ANY")
23-
return ListClass(name = className, generic = KotlinClass.ANY)
24+
return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true)
2425
}
2526
jsonArray.allItemAreNullElement() -> {
2627
LogUtil.i("$tag jsonArray allItemAreNullElement, return ListClass with generic type ${KotlinClass.ANY.name}")
27-
return ListClass(name = className, generic = KotlinClass.ANY)
28+
return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true)
2829
}
2930
jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> {
3031

@@ -34,25 +35,25 @@ class ListClassGeneratorByJSONArray(private val className: String, jsonArrayStri
3435
val p = jsonArrayExcludeNull[0].asJsonPrimitive;
3536
val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArrayExcludeNull) else p.toKotlinClass()
3637
LogUtil.i("$tag jsonArray allElementAreSamePrimitiveType, return ListClass with generic type ${elementKotlinClass.name}")
37-
return ListClass(name = className, generic = elementKotlinClass)
38+
return ListClass(name = className, generic = elementKotlinClass, nullableElements = hasNulls)
3839
}
3940
jsonArrayExcludeNull.allItemAreObjectElement() -> {
4041
val fatJsonObject = jsonArrayExcludeNull.getFatJsonObject()
4142
val itemObjClassName = "${className}Item"
4243
val dataClassFromJsonObj = DataClassGeneratorByJSONObject(itemObjClassName, fatJsonObject).generate()
4344
LogUtil.i("$tag jsonArray allItemAreObjectElement, return ListClass with generic type ${dataClassFromJsonObj.name}")
44-
return ListClass(className, dataClassFromJsonObj)
45+
return ListClass(className, dataClassFromJsonObj, nullableElements = hasNulls)
4546
}
4647
jsonArrayExcludeNull.allItemAreArrayElement() -> {
4748
val fatJsonArray = jsonArrayExcludeNull.getFatJsonArray()
4849
val itemArrayClassName = "${className}SubList"
4950
val listClassFromFatJsonArray = ListClassGeneratorByJSONArray(itemArrayClassName, fatJsonArray.toString()).generate()
5051
LogUtil.i("$tag jsonArray allItemAreArrayElement, return ListClass with generic type ${listClassFromFatJsonArray.name}")
51-
return ListClass(className, listClassFromFatJsonArray)
52+
return ListClass(className, listClassFromFatJsonArray, nullableElements = hasNulls)
5253
}
5354
else -> {
5455
LogUtil.i("$tag jsonArray exception shouldn't come here, return ListClass with generic type ANY")
55-
return ListClass(name = className, generic = KotlinClass.ANY)
56+
return ListClass(name = className, generic = KotlinClass.ANY, nullableElements = true)
5657
}
5758
}
5859
}

src/test/kotlin/wu/seal/jsontokotlin/DataClassGeneratorByJSONObjectTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ class DataClassGeneratorByJSONObjectTest {
147147

148148
val glossDefP2 = glossDefDataClass.properties[1]
149149
glossDefP2.name.should.be.equal("GlossSeeAlso")
150-
glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING).name)
150+
glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false).name)
151151
glossDefP2.originJsonValue.should.be.empty
152-
glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING))
152+
glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false))
153153
}
154154
}

src/test/kotlin/wu/seal/jsontokotlin/DataClassMakerTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ class DataClassMakerTest {
147147

148148
val glossDefP2 = glossDefDataClass.properties[1]
149149
glossDefP2.name.should.be.equal("GlossSeeAlso")
150-
glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING).name)
150+
glossDefP2.type.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false).name)
151151
glossDefP2.originJsonValue.should.be.empty
152-
glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING))
152+
glossDefP2.typeObject.should.be.equal(GenericListClass(KotlinClass.STRING, nullableElements = false))
153153

154154
}
155155

0 commit comments

Comments
 (0)