Skip to content

Commit

Permalink
fix #391
Browse files Browse the repository at this point in the history
  • Loading branch information
wuseal committed May 16, 2022
1 parent e95c820 commit 9e6a0dc
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra

private val tag = "ListClassGeneratorByJSONArray"
private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java)
private val jsonArrayExcludeNull = jsonArray.filterOutNullElement()


fun generate(): GenericListClass {

Expand All @@ -28,24 +30,24 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra
LogUtil.i("$tag jsonArray allItemAreNullElement, return GenericListClass with generic type ${KotlinClass.ANY.name}")
return GenericListClass(generic = KotlinClass.ANY)
}
jsonArray.allElementAreSamePrimitiveType() -> {
jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> {
// if all elements are numbers, we need to select the larger scope of Kotlin types among the elements
// e.g. [1,2,3.1] should return Double as it's type

val p = jsonArray[0].asJsonPrimitive;
val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArray) else p.toKotlinClass()
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)
}
jsonArray.allItemAreObjectElement() -> {
val fatJsonObject = getFatJsonObject(jsonArray)
jsonArrayExcludeNull.allItemAreObjectElement() -> {
val fatJsonObject = getFatJsonObject(jsonArrayExcludeNull)
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)
}
jsonArray.allItemAreArrayElement() -> {
val fatJsonArray = getFatJsonArray(jsonArray.map { it.asJsonArray })
jsonArrayExcludeNull.allItemAreArrayElement() -> {
val fatJsonArray = getFatJsonArray(jsonArrayExcludeNull.map { it.asJsonArray })
val genericListClassFromFatJsonArray = GenericListClassGeneratorByJSONArray(jsonKey, fatJsonArray.toString()).generate()
LogUtil.i("$tag jsonArray allItemAreArrayElement, return GenericListClass with generic type ${genericListClassFromFatJsonArray.name}")
return GenericListClass(generic = genericListClassFromFatJsonArray)
Expand Down Expand Up @@ -80,13 +82,18 @@ class GenericListClassGeneratorByJSONArray(private val jsonKey: String, jsonArra
val allFields = jsonArray.flatMap { it.asJsonObject.entrySet().map { entry -> Pair(entry.key, entry.value) } }
val fatJsonObject = JsonObject()
allFields.forEach { (key, value) ->
if (value is JsonNull && fatJsonObject.has(key)) {
if (value is JsonNull) {
//if the value is null and pre added the same key into the fatJsonObject,
// then translate it to a new special property to indicate that the property is nullable
//later will consume this property (do it here[DataClassGeneratorByJSONObject#consumeBackstageProperties])
// delete it or translate it back to normal property without [BACKSTAGE_NULLABLE_POSTFIX] when consume it
// and will not be generated in final code
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
if (fatJsonObject.has(key)) {
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
} else {
fatJsonObject.add(key, value)
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
}
} else if (fatJsonObject.has(key)) {
if (fatJsonObject[key].isJsonObject && value.isJsonObject) {//try not miss any fields of the key's related json object
val newValue = getFatJsonObject(JsonArray().apply { add(fatJsonObject[key]);add(value) })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class ListClassGeneratorByJSONArray(private val className: String, jsonArrayStri

private val tag = "ListClassGeneratorByJSONArray"
private val jsonArray: JsonArray = Gson().fromJson(jsonArrayString, JsonArray::class.java)
private val jsonArrayExcludeNull = jsonArray.filterOutNullElement()

fun generate(): ListClass {

Expand All @@ -25,25 +26,25 @@ class ListClassGeneratorByJSONArray(private val className: String, jsonArrayStri
LogUtil.i("$tag jsonArray allItemAreNullElement, return ListClass with generic type ${KotlinClass.ANY.name}")
return ListClass(name = className, generic = KotlinClass.ANY)
}
jsonArray.allElementAreSamePrimitiveType() -> {
jsonArrayExcludeNull.allElementAreSamePrimitiveType() -> {

// if all elements are numbers, we need to select the larger scope of Kotlin types among the elements
// e.g. [1,2,3.1] should return Double as it's type

val p = jsonArray[0].asJsonPrimitive;
val elementKotlinClass = if(p.isNumber) getKotlinNumberClass(jsonArray) else p.toKotlinClass()
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)
}
jsonArray.allItemAreObjectElement() -> {
val fatJsonObject = getFatJsonObject(jsonArray)
jsonArrayExcludeNull.allItemAreObjectElement() -> {
val fatJsonObject = getFatJsonObject(jsonArrayExcludeNull)
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)
}
jsonArray.allItemAreArrayElement() -> {
val fatJsonArray = getFatJsonArray(jsonArray)
jsonArrayExcludeNull.allItemAreArrayElement() -> {
val fatJsonArray = getFatJsonArray(jsonArrayExcludeNull)
val itemArrayClassName = "${className}SubList"
val listClassFromFatJsonArray = ListClassGeneratorByJSONArray(itemArrayClassName, fatJsonArray.toString()).generate()
LogUtil.i("$tag jsonArray allItemAreArrayElement, return ListClass with generic type ${listClassFromFatJsonArray.name}")
Expand Down Expand Up @@ -78,13 +79,18 @@ class ListClassGeneratorByJSONArray(private val className: String, jsonArrayStri
val allFields = jsonArray.flatMap { it.asJsonObject.entrySet().map { entry -> Pair(entry.key, entry.value) } }
val fatJsonObject = JsonObject()
allFields.forEach { (key, value) ->
if (value is JsonNull && fatJsonObject.has(key)) {
if (value is JsonNull ) {
//if the value is null and pre added the same key into the fatJsonObject,
// then translate it to a new special property to indicate that the property is nullable
//later will consume this property (do it here[DataClassGeneratorByJSONObject#consumeBackstageProperties])
// delete it or translate it back to normal property without [BACKSTAGE_NULLABLE_POSTFIX] when consume it
// and will not be generated in final code
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
if (fatJsonObject.has(key)) {
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
} else {
fatJsonObject.add(key, value)
fatJsonObject.add(key + BACKSTAGE_NULLABLE_POSTFIX, value)
}
} else if (fatJsonObject.has(key) && value is JsonPrimitive && value.isNumber
&& fatJsonObject[key].isJsonPrimitive && fatJsonObject[key].asJsonPrimitive.isNumber) {
//when the the field is a number type, we need to select the value with largest scope
Expand Down
61 changes: 61 additions & 0 deletions src/test/kotlin/wu/seal/jsontokotlin/regression/Issue391Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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.PropertyTypeStrategy
import wu.seal.jsontokotlin.model.TargetJsonConverter
import wu.seal.jsontokotlin.test.TestConfig
import wu.seal.jsontokotlin.utils.BaseTest

class Issue391Test :BaseTest(){


@Test
fun testIssue391() {
val json = """
[
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "http://localhost:8000/api/active-announcements?page=1",
"label": "1",
"active": true
},
{
"url": "http://localhost:8000/api/active-announcements?page=2",
"label": "2",
"active": false
},
{
"url": "http://localhost:8000/api/active-announcements?page=3",
"label": "3",
"active": false
},
]
""".trimIndent()
val expected = """
class Test : ArrayList<TestItem>()
@JsonClass(generateAdapter = true)
data class TestItem(
@Json(name = "active")
val active: Boolean = false,
@Json(name = "label")
val label: String = "",
@Json(name = "url")
val url: String? = null
)
""".trimIndent()
TestConfig.targetJsonConvertLib = TargetJsonConverter.MoshiCodeGen
TestConfig.isCommentOff = true
TestConfig.isNestedClassModel = false
TestConfig.defaultValueStrategy = DefaultValueStrategy.AllowNull
TestConfig.propertyTypeStrategy = PropertyTypeStrategy.AutoDeterMineNullableOrNot
json.generateKotlinClassCode("Test").should.equal(expected)
}
}

0 comments on commit 9e6a0dc

Please sign in to comment.