Related
I have two tables like as I described below
object Categories : LongIdTable() {
val name = varchar(name = "name", length = 50).uniqueIndex()
val count = integer(name = "count")
val page = integer(name = "page")
}
object Images : LongIdTable() {
val color = varchar(name = "color", length = 100)
val blurHash = varchar(name = "blurHash", length = 200)
val unsplashId = varchar(name = "unsplashId", length = 200)
val category = reference(name = "category", Categories.name).nullable()
val isAvailable = bool(name = "isAvailable")
val imagePath = varchar(name = "imagePath", length = 500)
}
and here is my Image & Category data classes
#Serializable
data class Image(
val id: Long,
val unsplashId: String,
val isAvailable: Boolean = false,
val color: String,
val blurHash: String,
var category: Category?,
val imagePath: String,
)
#Serializable
data class Category(
val id: Long,
val name: String,
val count: Int,
var imagePath: String?,
val page: Int
)
when I want to find images by their id I can't map category attribute
fun getImageById(id: Long): Image {
val images = ArrayList<Image>()
var categoryName = ""
transaction {
Images.select {
Images.id eq id
}.map { row ->
categoryName = row[Images.category]!!
images.add(
Image(
id = row[Images.id].value,
unsplashId = row[Images.unsplashId],
isAvailable = false,
color = row[Images.color],
blurHash = row[Images.blurHash],
category = null,
imagePath = row[Images.imagePath]
)
)
}
images[0].category = categoryRepository.getCategoryByName(categoryName)
}
return images[0]
}
how can I solve this priblem?
I am trying to define a Kotlin sealed class which consists of a number of data classes. The latter are used to define data transfer objects (DTO) representing the mySQL tables in a room database. I introduced the sealed class to generalize the different DTOs and be able to refer to them all by their supertype (DTO - the common properties each specific DTO has, eg. "id", etc.).
This compiles alright, but I don't think Kotlin understands that the data classes are the "subclasses" of the sealed class - no matter whether I defined them all in the same file as the sealed (parent) class, or - the preferred choice - in the same package... both options should be valid choices, according to the Kotlin documentation.
Any idea, where I'm going wrong here? Thanks.
Code:
package com.tanfra.shopmob.smob.data.local.dto
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.RewriteQueriesToDropUnusedColumns
import com.tanfra.shopmob.smob.data.local.utils.*
/**
* supertype, common to all DTO types - generic part of any DTO class
* (properties declared abstract --> implementation delegated to inheriting concrete class)
*/
sealed class Dto {
abstract val id: String
abstract var itemStatus: SmobItemStatus
abstract var itemPosition: Long
}
#Entity(tableName = "smobGroups")
#RewriteQueriesToDropUnusedColumns
data class SmobGroupDTO(
#PrimaryKey #ColumnInfo(name = "groupId") override val id: String = "invalid smob group entry",
#ColumnInfo(name = "groupItemStatus") override var itemStatus: SmobItemStatus = SmobItemStatus.NEW,
#ColumnInfo(name = "groupItemPosition") override var itemPosition: Long = -1L,
#ColumnInfo(name = "groupName") var name: String = "",
#ColumnInfo(name = "groupDescription") var description: String? = "",
#ColumnInfo(name = "groupType") var type: GroupType = GroupType.OTHER,
#ColumnInfo(name = "groupMembers") var members: List<String> = listOf(),
#ColumnInfo(name = "groupActivityDate") var activityDate: String = "",
#ColumnInfo(name = "groupActivityReps") var activityReps: Long = 0,
) : Dto()
#Entity(tableName = "smobLists")
#RewriteQueriesToDropUnusedColumns
data class SmobListDTO(
#PrimaryKey #ColumnInfo(name = "listId") override val id: String = "invalid smob list id",
#ColumnInfo(name = "listItemStatus") override var itemStatus: SmobItemStatus = SmobItemStatus.NEW,
#ColumnInfo(name = "listItemPosition") override var itemPosition: Long = -1L,
#ColumnInfo(name = "listName") var name: String = "",
#ColumnInfo(name = "listDescription") var description: String? = "",
#ColumnInfo(name = "listItems") var items: List<SmobListItem> = listOf(),
#ColumnInfo(name = "listMembers") var members: List<String> = listOf(),
#ColumnInfo(name = "listLifecycleStatus") var lcStatus: SmobItemStatus = SmobItemStatus.OPEN,
#ColumnInfo(name = "listLifecycleCompletion") var lcCompletion: Double = -1.0,
) : Dto()
#Entity(tableName = "smobProducts")
#RewriteQueriesToDropUnusedColumns
data class SmobProductDTO(
#PrimaryKey #ColumnInfo(name = "productId") override val id: String = "invalid smob product id",
#ColumnInfo(name = "productItemStatus") override var itemStatus: SmobItemStatus = SmobItemStatus.NEW,
#ColumnInfo(name = "productItemPosition") override var itemPosition: Long = -1L,
#ColumnInfo(name = "productName") var name: String = "",
#ColumnInfo(name = "productDescription") var description: String? = "",
#ColumnInfo(name = "productImageUrl") var imageUrl: String? = "",
#ColumnInfo(name = "productCategoryMain") var categoryMain: ProductMainCategory = ProductMainCategory.OTHER,
#ColumnInfo(name = "productCategorySub") var categorySub: ProductSubCategory = ProductSubCategory.OTHER,
#ColumnInfo(name = "productActivityDate") var activityDate: String = "",
#ColumnInfo(name = "productActivityReps") var activityReps: Long = 0L,
#ColumnInfo(name = "productInShopCategory") var inShopCategory: ShopCategory = ShopCategory.OTHER,
#ColumnInfo(name = "productInShopName") var inShopName: String = "dummy shop",
#ColumnInfo(name = "productInShopLocation") var inShopLocation: ShopLocation = ShopLocation(0.0, 0.0),
) : Dto()
#Entity(tableName = "smobShops")
#RewriteQueriesToDropUnusedColumns
data class SmobShopDTO(
#PrimaryKey #ColumnInfo(name = "shopId") override val id: String = "invalid smob shop id",
#ColumnInfo(name = "shopItemStatus") override var itemStatus: SmobItemStatus = SmobItemStatus.NEW,
#ColumnInfo(name = "shopItemPosition") override var itemPosition: Long = -1L,
#ColumnInfo(name = "shopName") var name: String = "",
#ColumnInfo(name = "shopDescription") var description: String? = "",
#ColumnInfo(name = "shopImageUrl") var imageUrl: String? = "",
#ColumnInfo(name = "shopLocationLatitude") var locLat: Double = 0.0,
#ColumnInfo(name = "shopLocationLongitude") var locLong: Double = 0.0,
#ColumnInfo(name = "shopType") var type: ShopType = ShopType.INDIVIDUAL,
#ColumnInfo(name = "shopCategory") var category: ShopCategory = ShopCategory.OTHER,
#ColumnInfo(name = "shopBusiness") var business: List<String> = listOf()
) : Dto()
#Entity(tableName = "smobUsers")
#RewriteQueriesToDropUnusedColumns
data class SmobUserDTO(
#PrimaryKey #ColumnInfo(name = "userId") override val id: String = "invalid smob user id",
#ColumnInfo(name = "userItemStatus") override var itemStatus: SmobItemStatus = SmobItemStatus.NEW,
#ColumnInfo(name = "userItemPosition") override var itemPosition: Long = -1L,
#ColumnInfo(name = "userUsername") var username: String = "",
#ColumnInfo(name = "userName") var name: String = "",
#ColumnInfo(name = "userEmail") var email: String = "",
#ColumnInfo(name = "userImageUrl") var imageUrl: String? = ""
) : Dto()
The reason, I believe Kotlin didn't make the desired connection between the sealed class and the data classes (= subclasses) is that it still asks me for an "else" branch in "when" expressions which act upon the members of the sealed class:
package com.tanfra.shopmob.smob.data.net.nto2dto
import com.tanfra.shopmob.smob.data.local.dto.*
import com.tanfra.shopmob.smob.data.net.nto.*
import com.tanfra.shopmob.smob.data.repo.ato.Ato
// ATO --> DTO
fun <DTO: Dto, ATO: Ato> ATO._asDatabaseModel(d: DTO): DTO? {
return when (d) {
is SmobGroupDTO -> {
SmobGroupDTO(
id = (this as SmobGroupNTO).id,
itemStatus = this.itemStatus,
itemPosition = this.itemPosition,
name = this.name,
description = this.description,
type = this.type,
members = this.members,
activityDate = this.activity.date,
activityReps = this.activity.reps,
) as DTO
}
is SmobListDTO -> {
SmobListDTO(
id = (this as SmobListNTO).id,
itemStatus = this.itemStatus,
itemPosition = this.itemPosition,
name = this.name,
description = this.description,
items = this.items,
members = this.members,
lcStatus = this.lifecycle.status,
lcCompletion = this.lifecycle.completion,
) as DTO
}
is SmobProductDTO -> {
SmobProductDTO(
id = (this as SmobProductNTO).id,
itemStatus = this.itemStatus,
itemPosition = this.itemPosition,
name = this.name,
description = this.description,
imageUrl = this.imageUrl,
categoryMain = this.category.main,
categorySub = this.category.sub,
activityDate = this.activity.date,
activityReps = this.activity.reps,
inShopCategory = this.inShop.category,
inShopName = this.inShop.name,
inShopLocation = this.inShop.location,
) as DTO
}
is SmobShopDTO -> {
SmobShopDTO(
id = (this as SmobShopNTO).id,
itemStatus = this.itemStatus,
itemPosition = this.itemPosition,
name = this.name,
description = this.description,
imageUrl = this.imageUrl,
locLat = this.location.latitude,
locLong = this.location.longitude,
type = this.type,
category = this.category,
business = this.business,
) as DTO
}
is SmobUserDTO -> {
SmobUserDTO(
id = (this as SmobUserNTO).id,
itemStatus = this.itemStatus,
itemPosition = this.itemPosition,
username = this.username,
name = this.name,
email = this.email,
imageUrl = this.imageUrl,
) as DTO
}
else -> null
} // when(DTO) ... resolving generic type to concrete type
}
It's caused by your use of generics on the method signature :
fun <DTO: Dto, ATO: Ato> ATO._asDatabaseModel(d: DTO): DTO?
There's a good thread on Reddit which is very like your example. See here:
https://www.reddit.com/r/Kotlin/comments/ei8zh5/kotlin_requires_else_branch_in_when_statement/
So, to solve your problem, just change the method signature to return a type of DTO not DTO?
It's almost as if the compiler is forgetting that the DTO is a sealed class when you make it a generic parameter, so you need an exhaustive check.
As you as using is in a when statement Kotlin will smart cast the DTO to the right type anyway, so no need for the generic argument.
Here's a cut down example based on your code that works without the else:
package paul.sealed
sealed class DTO {
abstract val id: String
}
data class SmobGroupDTO(override val id: String = "invalid smob user id", val name: String = "") : DTO()
data class SmobListDTO(override val id: String = "invalid smob user id", val name: String = "") : DTO()
fun main() {
fun processDTO(dto: DTO): String {
return when (dto) {
is SmobGroupDTO -> "Group"
is SmobListDTO -> "List"
}
}
}
In my Kotlin Android codebase I have the following 2 entities..
#Entity(
tableName = ModuleConfiguration.tableName,
primaryKeys = [ModuleConfiguration.COL_ID],
foreignKeys = arrayOf(
ForeignKey(
entity = Module::class,
parentColumns = [Module.COL_ID],
childColumns = [ModuleConfiguration.COL_MODULE_ID],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Group::class,
parentColumns = [Group.COL_ID],
childColumns = [ModuleConfiguration.COL_GROUP_ID]
)
)
)
class ModuleConfiguration(
#ColumnInfo(name = COL_ID)
var id: String = UUID.randomUUID().toString(),
#ColumnInfo(name = COL_TABLE)
var table: String,
#ColumnInfo(name = COL_FIELD_NAME)
var fieldName: String,
#ColumnInfo(name = COL_FIELD_LABEL)
var fieldLabel: String,
#ColumnInfo(name = COL_FIELD_TYPE)
var fieldType: ModuleConfigurationItemType,
#ColumnInfo(name = COL_GROUP_ID)
var groupId: String?,
#ColumnInfo(name = COL_MODULE_ID)
var moduleId: String,
#ColumnInfo(name = COL_POSITION)
var position: Int,
#ColumnInfo(name = COL_VISIBLE)
var visible: Int = 1, //Usually visible
#ColumnInfo(name = COL_READ_ONLY)
var readOnly: Int = 0, //Usually false
#ColumnInfo(name = COL_REQUIRED)
var required: Int = 0, //Usually false
#ColumnInfo(name = COL_CREATED_AT)
var createdAt: Long = CustomDateTimeUtil.getTodayInUTC(),
#ColumnInfo(name = COL_UPDATED_AT)
var updatedAt: Long = CustomDateTimeUtil.getTodayInUTC()
) : Cloneable, Serializable, Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.readString() ?: "",
fieldType = ModuleConfigurationItemType.valueOf(parcel.readString() ?: FieldType.UNKNOWN.name),
groupId = parcel.readString(),
moduleId = parcel.readString() ?: "",
position = parcel.readInt(),
visible = parcel.readInt(),
readOnly = parcel.readInt(),
required = parcel.readInt(),
createdAt = parcel.readLong(),
updatedAt = parcel.readLong()
) {
}
fun getViewType() : ModuleConfigurationItemType {
return this.fieldType
}
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
override fun hashCode(): Int {
return super.hashCode()
}
override fun clone(): Any {
return super.clone()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(id)
parcel.writeString(table)
parcel.writeString(fieldName)
parcel.writeString(fieldLabel)
parcel.writeString(fieldType.name)
parcel.writeString(groupId)
parcel.writeString(moduleId)
parcel.writeInt(position)
parcel.writeInt(visible)
parcel.writeInt(readOnly)
parcel.writeInt(required)
parcel.writeLong(createdAt)
parcel.writeLong(updatedAt)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ModuleConfiguration> {
const val tableName = "module_configuration"
const val COL_ID = "id"
const val COL_MODULE_ID = "module_id"
const val COL_TABLE = "table"
const val COL_FIELD_NAME = "field_name"
const val COL_FIELD_LABEL = "field_label"
const val COL_FIELD_TYPE = "field_type"
const val COL_GROUP_ID = "group_id"
const val COL_VISIBLE = "visible"
const val COL_READ_ONLY = "read_only"
const val COL_REQUIRED = "required"
const val COL_POSITION = "position"
const val COL_CREATED_AT = "created_at"
const val COL_UPDATED_AT = "updated_at"
const val COL_CLIENT_ID = "client_id"
override fun createFromParcel(parcel: Parcel): ModuleConfiguration {
return ModuleConfiguration(parcel)
}
override fun newArray(size: Int): Array<ModuleConfiguration?> {
return arrayOfNulls(size)
}
}
}
and Group Entity
#Entity(
tableName = Group.tableName,
primaryKeys = [Group.COL_ID]
)
class Group(
#ColumnInfo(name = COL_ID)
var id: String = UUID.randomUUID().toString(),
#ColumnInfo(name = COL_NAME)
var name: String,
#ColumnInfo(name = COL_CREATED_AT)
var createdAt: Long = CustomDateTimeUtil.getTodayInUTC(),
#ColumnInfo(name = COL_UPDATED_AT)
var updatedAt: Long = CustomDateTimeUtil.getTodayInUTC()
) : Cloneable, Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.readLong(),
parcel.readLong()
) {
}
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
override fun hashCode(): Int {
return super.hashCode()
}
override fun clone(): Any {
return super.clone()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(id)
parcel.writeString(name)
parcel.writeLong(createdAt)
parcel.writeLong(updatedAt)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Group> {
const val tableName = "group"
const val COL_ID = "id"
const val COL_NAME = "name"
const val COL_CREATED_AT = "created_at"
const val COL_UPDATED_AT = "updated_at"
const val COL_CLIENT_ID = "client_id"
override fun createFromParcel(parcel: Parcel): Group {
return Group(parcel)
}
override fun newArray(size: Int): Array<Group?> {
return arrayOfNulls(size)
}
}
}
My problem is that I am trying to set nullable foreign key, as you can see in entity named ModuleConfiguration, there is column named group_id, which is nullable.
#ColumnInfo(name = COL_GROUP_ID)
var groupId: String?,
Since, this column belongs to other entity named Group, I am trying to make it foreign key, but when I do that, I am getting following error
SQLiteConstraintException: FOREIGN KEY constraint failed (code 787)
On searching online, I found following answers on stackoverflow:
Android with Room - How to set a foreign key nullable
or
Nullable foreign key on room
but this did not help me as these answer suggest to change primitive types like int or long into non-primitive types like Integer or Long for allowing them to be null. I am already using String as type.
any help would be highly appreciated.
Thanks in advance.
How can I do a recursive / deep merge of two data classes in Kotlin? Something like this:
import kotlin.reflect.*
import kotlin.reflect.full.*
data class Address(
val street: String? = null,
val zip: String? = null
)
data class User(
val name: String? = null,
val age: Int? = null,
val address: Address? = null
)
inline fun <reified T : Any> T.merge(other: T): T {
val nameToProperty = T::class.declaredMemberProperties.associateBy { it.name }
val primaryConstructor = T::class.primaryConstructor!!
val args = primaryConstructor.parameters.associate { parameter ->
val property = nameToProperty[parameter.name]!!
val type = property.returnType.classifier as KClass<*>
if (type.isData) {
parameter to this.merge(other) //inline function can't be recursive
} else {
parameter to (property.get(other) ?: property.get(this))
}
}
return primaryConstructor.callBy(args)
}
val u1 = User(name = "Tiina", address = Address(street = "Hämeenkatu"))
val u2 = User(age = 23, address = Address(zip = "33100"))
u1.merge(u2)
// expected: User(age = 23, name= "Tiina", address = Address(zip = "33100", street = "Hämeenkatu")
related: Combining/merging data classes in Kotlin
There were several problems in the posted code,
unnecessary reification and inlining
when type isData was detected instead of merging the values of the property merge on this with the other was called, so it became endless recursion.
get cannot be used on KProperty1<out T, Any?> because of the variance
some non-idiomatic stuff which works, but can be made better
Here's the fixed version. For production I would've added some checks and error messages, but this should work for "happy path" and hopefully give you the base to build on:
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.primaryConstructor
data class Address(
val street: String? = null,
val zip: String? = null
)
data class User(
val name: String? = null,
val age: Int? = null,
val address: Address? = null,
val map: Map<String, Int>? = null
)
fun <T> mergeData(property: KProperty1<out T, Any?>, left: T, right: T): Any? {
val leftValue = property.getter.call(left)
val rightValue = property.getter.call(right)
return rightValue?.let {
if ((property.returnType.classifier as KClass<*>).isSubclassOf(Map::class)) (leftValue as? Map<*, *>)?.plus(it as Map<*, *>)
else leftValue?.merge(it)
} ?: rightValue ?: leftValue
}
fun <T> lastNonNull(property: KProperty1<out T, Any?>, left: T, right: T) =
property.getter.call(right) ?: property.getter.call(left)
fun <T : Any> T.merge(other: T): T {
val nameToProperty = this::class.declaredMemberProperties.associateBy { it.name }
val primaryConstructor = this::class.primaryConstructor!!
val args: Map<KParameter, Any?> = primaryConstructor.parameters.associateWith { parameter ->
val property = nameToProperty[parameter.name]!!
val type = property.returnType.classifier as KClass<*>
when {
type.isData || type.isSubclassOf(Map::class) -> mergeData(property, this, other)
else -> lastNonNull(property, this, other)
}
}
return primaryConstructor.callBy(args)
}
// verification
val u1 = User(name = "Tiina", address = Address(street = "Hämeenkatu"), map = mapOf("a" to 1))
val u2 = User(age = 23, address = Address(zip = "33100"), map = mapOf("b" to 2))
check(
u1.merge(u2) == User(
age = 23,
name = "Tiina",
address = Address(zip = "33100", street = "Hämeenkatu"),
map = mapOf("a" to 1,"b" to 2)
)
) {
"doesn't work"
}
println("Works!")
I am trying to deserialize a JSON into a kotlin class using kotlin.serialization. However when the code goes to deserialize the json it throws the error kotlinx.serialization.SerializationException: Any type is not supported
Can anyone help me resolves this?
Product:
#Serializable
data class Product(
val id: Int = 0,
val name: String = "",
val slug: String = "",
val permalink: String = "",
#SerialName("date_created") val dateCreated: String = "",
#SerialName("date_created_gmt") val dateCreatedGmt: String = "",
#SerialName("date_modified") val dateModified: String = "",
#SerialName("date_modified_gmt") val dateModifiedGmt: String = "",
val type: String = "",
val status: String = "",
val featured: Boolean = false,
#SerialName("catalog_visibility") val catalogVisibility: String = "",
val description: String = "",
#SerialName("short_description") val shortDescription: String = "",
val sku: String = "",
val price: String = "",
#SerialName("regular_price") val regularPrice: String = "",
#SerialName("sale_price") val salePrice: String = "",
#SerialName("on_sale") val onSale: Boolean = false,
val purchasable: Boolean = false,
#SerialName("total_sales") val totalSales: Int = 0,
#SerialName("external_url") val externalUrl: String = "",
#SerialName("tax_status") val taxStatus: String = "",
#SerialName("tax_class") val taxClass: String = "",
#SerialName("stock_quantity") val stockQuantity: String = "",
#SerialName("stock_status") val stockStatus: String = "",
val backorders: String = "",
#SerialName("backorders_allowed") val backordersAllowed: Boolean = false,
val backordered: Boolean = false,
#SerialName("sold_individually") val soldIndividually: Boolean = false,
val weight: String = "",
val dimensions: ProductDimensions = ProductDimensions(),
#SerialName("shipping_required") val shippingRequired: Boolean = false,
#SerialName("shipping_taxable") val shippingTaxable: Boolean = false,
#SerialName("shipping_class") val shippingClass: String = "",
#SerialName("shipping_class_id") val shippingClassId: Int = 0,
#SerialName("reviews_allowed") val reviewsAllowed: Boolean = false,
#SerialName("average_rating") val averageRating: String = "",
#SerialName("rating_count") val ratingCount: Int = 0,
#SerialName("related_ids") val relatedIds: List<Int> = listOf(),
#SerialName("upsell_ids") val upsellIds: List<Int> = listOf(),
#SerialName("cross_sell_ids") val crossSellIds: List<Int> = listOf(),
#SerialName("parent_id") val parentId: Int = 0,
#SerialName("purchase_note") val purchaseNote: String = "",
val categories: List<ProductCategory> = listOf(),
val images: List<ProductImage> = listOf(),
val attributes: List<ProductAttribute> = listOf(),
val variations: List<Int> = listOf()
)
ProductDimension:
data class ProductDimensions(
val length: String = "",
val width: String = "",
val height: String = ""
)
Product Attribute:
data class ProductAttribute(
val id: Int = 0,
val name: String = "",
val position: Int = 0,
val visible: Boolean = false,
val variation: Boolean = false,
val options: List<String> = listOf()
)
Product Category:
data class ProductCategory(
val id: Int = 0,
val name: String = "",
val slug: String = ""
)
Product Image:
data class ProductImage(
val id: Int = 0,
#SerialName("date_created") val dateCreated: String = "",
#SerialName("date_created_gmt") val dateCreatedGmt: String = "",
#SerialName("date_modified") val dateModified: String = "",
#SerialName("date_modified_gmt") val dateModifiedGmt: String = "",
val src: String = "",
val name: String = "",
val alt: String = ""
)
And the associated json is on pastebin here
Here is a working cloud9 (simply run ./gradlew run) solution using the last version of Kotlin (1.3.0) and the serialization runtime (0.9.0)
val serializer = Product.serializer().list
val json = JSON.nonstrict.parse<List<Product>>(serializer, jsonString)
Note that some attributes had to have the #Optional annotation added for this serialization to work with your sample, hence the nonstrict version of the parsing.
#Serializable
data class Product(
val id: Int = 0,
val name: String = "",
val slug: String = "",
val permalink: String = "",
#Optional #SerialName("date_created") val dateCreated: String = "",
#SerialName("date_created_gmt") val dateCreatedGmt: String = "",
#SerialName("date_modified") val dateModified: String = "",
#SerialName("date_modified_gmt") val dateModifiedGmt: String = "",
val type: String = "",
val status: String = "",
val featured: Boolean = false,
#SerialName("catalog_visibility") val catalogVisibility: String = "",
val description: String = "",
#SerialName("short_description") val shortDescription: String = "",
val sku: String = "",
val price: String = "",
#SerialName("regular_price") val regularPrice: String = "",
#SerialName("sale_price") val salePrice: String = "",
#SerialName("on_sale") val onSale: Boolean = false,
val purchasable: Boolean = false,
#SerialName("total_sales") val totalSales: Int = 0,
#SerialName("external_url") val externalUrl: String = "",
#SerialName("tax_status") val taxStatus: String = "",
#SerialName("tax_class") val taxClass: String = "",
#SerialName("stock_quantity") val stockQuantity: String = "",
#Optional #SerialName("stock_status") val stockStatus: String = "",
val backorders: String = "",
#SerialName("backorders_allowed") val backordersAllowed: Boolean = false,
val backordered: Boolean = false,
#SerialName("sold_individually") val soldIndividually: Boolean = false,
val weight: String = "",
val dimensions: ProductDimensions = ProductDimensions(),
#SerialName("shipping_required") val shippingRequired: Boolean = false,
#SerialName("shipping_taxable") val shippingTaxable: Boolean = false,
#SerialName("shipping_class") val shippingClass: String = "",
#SerialName("shipping_class_id") val shippingClassId: Int = 0,
#SerialName("reviews_allowed") val reviewsAllowed: Boolean = false,
#SerialName("average_rating") val averageRating: String = "",
#SerialName("rating_count") val ratingCount: Int = 0,
#SerialName("related_ids") val relatedIds: List<Int> = listOf(),
#SerialName("upsell_ids") val upsellIds: List<Int> = listOf(),
#SerialName("cross_sell_ids") val crossSellIds: List<Int> = listOf(),
#SerialName("parent_id") val parentId: Int = 0,
#SerialName("purchase_note") val purchaseNote: String = "",
val categories: List<ProductCategory> = listOf(),
val images: List<ProductImage> = listOf(),
val attributes: List<ProductAttribute> = listOf(),
val variations: List<Int> = listOf()
)
Notice: date_created, stock_status
A good online tool to generate your data class based on your json raw data is quicktype.io
Edit: Just figured out that cloud9 had been acquired by amazon two years ago, and you can no longer create an account to see public workspace.
So here is the link to the filesystem
I've just tried this and couldn't replicate your problem. Can you maybe provide a github repo where we can see the problem?
There was one issue in the JSON in your pastebin link, which was that the stock_status value was missing, so I hit this error: kotlinx.serialization.MissingFieldException: Field stock_status is required, but it was missing
However once I added that value into the JSON I could deserialize it fine.
Here's the code I used:
import kotlinx.serialization.*
import kotlinx.serialization.json.JSON
import java.io.File
val product = JSON(strictMode = false).parseList<Product>(File("./serializationTest.json").readText())
The other thing is that according to the documentation
Using Kotlin Serialization requires Kotlin compiler 1.3.0 or higher.
Kotlin 1.3 was only officially released very recently, so are you maybe using a release candidate or something, instead of the formal release (with which it worked for me)? Maybe update all your dependencies to the latest versions and try again?