I am employing mapstruct to map between my network and database data objects within my current Android project.
api 'org.mapstruct:mapstruct:1.5.2.Final'
kapt 'org.mapstruct:mapstruct-processor:1.5.2.Final'
my network data class resembles this
#Keep
#Serializable
data class Item(
#SerialName("actionTested")
val actionTested: String? = null,
#SerialName("agonistAntagonist")
val agonistAntagonist: String = "",
#SerialName("cellOrigin")
val cellOrigin: String? = null,
#SerialName("cmmnt")
val comment: String? = null,
#SerialName("displayDose")
val displayDose: String = "",
#SerialName("displayValue")
val displayValue: String = "",
#SerialName("document")
val document: Document = Document(),
#SerialName("documentYear")
val documentYear: Int = 0,
#SerialName("dose")
val dose: String = "",
#SerialName("doseUnit")
val doseUnit: String? = null,
#SerialName("drug")
val drug: String = "",
#SerialName("errorType")
val errorType: String? = null,
#SerialName("errorValue")
val errorValue: String? = null,
#SerialName("experimentType")
val experimentType: String? = null,
#SerialName("hash")
val hash: String = "",
#SerialName("id")
val id: String = "",
#SerialName("isPrimaryTarget")
val isPrimaryTarget: String? = null,
#SerialName("mutationDetail")
val mutationDetail: String? = null,
#SerialName("otherExpDetails")
val otherExpDetails: String? = null,
#SerialName("parameter")
val parameter: String = "",
#SerialName("sharpN")
val sharpN: String? = null,
#SerialName("smiles")
val smiles: String? = null,
#SerialName("source")
val source: String = "",
#SerialName("specie")
val specie: String = "",
#SerialName("strainRace")
val strainRace: String? = null,
#SerialName("studyType")
val studyType: String = "",
#SerialName("target")
val target: String = "",
#SerialName("targetType")
val targetType: String = "",
#SerialName("testSystem")
val testSystem: String = "",
#SerialName("transfection")
val transfection: String = "",
#SerialName("unitNormalized")
val unitNormalized: String? = null,
#SerialName("unitOriginal")
val unitOriginal: String? = null,
#SerialName("valueNormalized")
val valueNormalized: String = "",
#SerialName("valueOriginal")
val valueOriginal: String = ""
)
My database data object resembles this
data class ActivitySearchItemDO(
#ColumnInfo(name = "action_tested") val actionTested: String?,
#ColumnInfo(name = "agonist_antagonist") val agonistAntagonist: String,
#ColumnInfo(name = "cell_origin") val cellOrigin: String?,
#ColumnInfo(name = "comment") val comment: String?,
#ColumnInfo(name = "display_dose") val displayDose: String,
#ColumnInfo(name = "display_value") val displayValue: String,
#ColumnInfo(name = "document") val document: DocumentVO,
#ColumnInfo(name = "document_year") val documentYear: Int,
#ColumnInfo(name = "dose") val dose: String,
#ColumnInfo(name = "dose_unit") val doseUnit: String?,
#ColumnInfo(name = "drug") val drug: String,
#ColumnInfo(name = "error_type") val errorType: String?,
#ColumnInfo(name = "error_value") val errorValue: String?,
#ColumnInfo(name = "experiment_type") val experimentType: String?,
#ColumnInfo(name = "hash") val hash: String,
#ColumnInfo(name = "id") val id: String,
#ColumnInfo(name = "is_primary_target") val isPrimaryTarget: String? = null,
#ColumnInfo(name = "mutation_detail") val mutationDetail: String?,
#ColumnInfo(name = "other_exp_details") val otherExpDetails: String?,
#ColumnInfo(name = "parameter") val parameter: String,
#ColumnInfo(name = "sharp_N") val sharpN: String?,
#ColumnInfo(name = "smiles") val smiles: String?,
#ColumnInfo(name = "source") val source: String,
#ColumnInfo(name = "specie") val specie: String,
#ColumnInfo(name = "strain_race") val strainRace: String?,
#ColumnInfo(name = "study_type") val studyType: String,
#ColumnInfo(name = "target") val target: String,
#ColumnInfo(name = "target_type") val targetType: String,
#ColumnInfo(name = "test_system") val testSystem: String,
#ColumnInfo(name = "transfection") val transfection: String,
#ColumnInfo(name = "unit_normalized") val unitNormalized: String?,
#ColumnInfo(name = "unit_original") val unitOriginal: String?,
#ColumnInfo(name = "value_normalized") val valueNormalized: String,
#ColumnInfo(name = "value_original") val valueOriginal: String
)
when i build my project i get the following warning and do not understand why
warning: Unmapped target property: "isPrimaryTarget".
public abstract com.myapp.model.database.ActivitySearchItemDO map(#org.jetbrains.annotations.NotNull()
my mapper resembles this
#InheritInverseConfiguration(name = "map")
fun map(dataFields: Item): ActivitySearchItemDO
why is mapstruct reporting that the isPrimaryTarget is not being mapped?
UPDATE
it appears to be the "is" prefix that is causing the issue, when i remove is from the variable names it maps ok. which i am happy with as this field is not a boolean type but a String.
however i now have another case that is boolean and its generating a similar warning. why does mapstruct have an issue when mapping boolean fields.
Related
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"
}
}
}
Trying to convert errorBody() of retrofit error response to ResponseWrapper as follows:
retrofit.responseBodyConverter<ResponseWrapper<R>>(Failure::class.java, arrayOf(object : Annotation {}))
.convert(response.errorBody())
ResponseWrapper is as follows:
sealed class ResponseWrapper<R> {
data class Success<R>(val payload: R) : ResponseWrapper<R>()
data class Failure<R>(
#field:Json(name = "code") val code: Int = INVALID_CODE,
#field:Json(name = "status") val status: String = "",
#field:Json(name = "exception") val exception: String = "",
#field:Json(name = "message") val message: String = "",
#field:Json(name = "fieldErrors") val fieldErrors: List<FieldError> = emptyList()
) : ResponseWrapper<R>()
class NotFound<R> : ResponseWrapper<R>()
class NoInternet<R> : ResponseWrapper<R>()
data class FieldError(
#field:Json(name = "field") val field: String,
#field:Json(name = "message") val message: String,
#field:Json(name = "rejectValue") val rejectValue: String
) }
By doing this with Retrofit/Moshi, error message as follows is created:
java.lang.AbstractMethodError: abstract method "java.lang.Class java.lang.annotation.Annotation.annotationType()"
at retrofit2.converter.moshi.MoshiConverterFactory.jsonAnnotations(MoshiConverterFactory.java:122)
at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
What did I wrong here?
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?
data class Student(
val id: Int?,
val firstName: String?,
val lastName: String?,
val hobbyId: Int?,
val address1: String?,
val address2: String?,
val created: String?,
val updated: String?,
...
)
I have like above data class, and I want to create a Student instance with only first name and last name.
So If I run this,
// creating a student
Student(
firstName = "Mark"
lastName = "S"
)
I will get No value passed for parameter 'id' ... errors.
To avoid that, I modified the Student class like this,
data class Student(
val id: Int? = null,
val firstName: String? = null,
val lastName: String? = null,
val hobbyId: Int? = null,
val address1: String? = null,
val address2: String? = null,
val created: String? = null,
val updated: String? = null,
...
)
But it looks so ugly.
Is there any better way?
You can set default values in your primary constructor as shown below.
data class Student(val id: Int = Int.MIN_VALUE,
val firstName: String,
val lastName: String,
val hobbyId: Int = Int.MIN_VALUE,
val address1: String = "",
val address2: String = "",
val created: String = "",
val updated: String = "")
Then you can use named arguments when creating a new student instance as shown below.
Student(firstName = "Mark", lastName = "S")
I am not sure the solution I am giving you is the best or not. But definitely neat.
The only thing I don't like to go with nulls as default param, because Kotlin offers Null Safety, lets not remove it just because to fulfil some other requirement. Mark them null only if they can be null. Else old Java way is good. Initialize them with some default value.
data class Student(val id: Int,
val firstName: String,
val lastName: String,
val hobbyId: Int,
val address1: String,
val address2: String,
val created: String,
val updated: String) {
constructor(firstName: String, lastName: String) :
this(Int.MIN_VALUE, firstName, lastName, Int.MIN_VALUE, "", "", "", "")
}
data class InLog(
var clock_in_lat: String?="None",
var clock_in_lng: String?="None",
var clock_out_lat: String?="None",
val clock_out_lng: String?="None",
val created_at: String?="None",
val duration: String?="None",
val end_time: String?="None",
val id: Int?=Int.MIN_VALUE,
var late_duration: String? = "None",
val start_time: String?="None",
val type: String?="None",
val updated_at: String?="None",
val user_id: Int?=Int.MIN_VALUE)
in Kotlin we do like that remeber ? mark symbol use.
I'm trying to access a list of Items by the primary key of Feed like so. The primary key is a url. Attempting to access it like this gives me null back, and attepting to put :arg0 in quotes creates a compile time error error: Unused parameter: arg0. Is it possible to use a url for this query?
#Query("SELECT * FROM item WHERE feed_url = :arg0")
fun observeAllByFeed(feedUrl: String): LiveData<List<Item>>
Item
#Entity(foreignKeys = arrayOf(ForeignKey(
entity = Feed::class,
parentColumns = arrayOf("url"),
childColumns = arrayOf("feed_url"))))
data class Item(
#ColumnInfo(name = "guid") #PrimaryKey var guid: String = "",
#ColumnInfo(name = "categoryIds") var categoryIds: List<Int> = emptyList(),
#ColumnInfo(name = "comments") var comments: String? = null,
#ColumnInfo(name = "content") var content: String? = null,
#ColumnInfo(name = "description") var description: String = "",
#ColumnInfo(name = "author") var author: String = "",
#ColumnInfo(name = "block") var block: Boolean = false,
#ColumnInfo(name = "duration") var duration: Int = 0,
#ColumnInfo(name = "explicit") var explicit: String = "",
#ColumnInfo(name = "image_url") var imageUrl: String = "",
#ColumnInfo(name = "keywords") var keywords: List<String> = emptyList(),
#ColumnInfo(name = "subtitle") var subTitle: String = "",
#ColumnInfo(name = "summary") var summary: String = "",
#ColumnInfo(name = "link") var link: String = "",
#ColumnInfo(name = "publication_date") var pubDate: Date = Date(),
#ColumnInfo(name = "title") var title: String = "",
#ColumnInfo(name = "feed_url") var feedURL: String = "",
#ColumnInfo(name = "download_state") var downloadState: Int = Item.DownloadState.STATE_NOT_DOWNLOADED
) : Serializable {
companion object DownloadState {
val STATE_NOT_DOWNLOADED = 0
val STATE_DOWNLOADING = 1
val STATE_DOWNLOADED = 2
}
}
Feed
#Entity(foreignKeys = arrayOf(ForeignKey(
entity = Owner::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("owner_id")
)))
data class Feed(
#ColumnInfo(name = "url") #PrimaryKey var URL: String = "",
#ColumnInfo(name = "copyright") var copyright: String? = null,
#ColumnInfo(name = "description") var description: String? = null,
#ColumnInfo(name = "itemGUIDs") var itemGUIDs: List<String> = emptyList(),
#ColumnInfo(name = "author") var author: String? = null,
#ColumnInfo(name = "block") var block: Boolean = false,
#ColumnInfo(name = "categoryIds") var categoryIds: List<Int> = emptyList(),
#ColumnInfo(name = "explicit") var explicit: Boolean = false,
#ColumnInfo(name = "image_url") var imageUrl: String? = null,
#ColumnInfo(name = "owner_id") var ownerId: Int = -1,
#ColumnInfo(name = "subtitle") var subtitle: String? = null,
#ColumnInfo(name = "summary") var summary: String? = null,
#ColumnInfo(name = "language") var language: String = "",
#ColumnInfo(name = "link") var link: String = "",
#ColumnInfo(name = "title") var title: String = "",
#ColumnInfo(name = "expiry") var expiry: Date = Date(),
#ColumnInfo(name = "subscribed") var subscribed: Boolean = false
) : Serializable
Owner
#Entity
data class Owner(
#ColumnInfo(name = "name") var name: String? = null,
#ColumnInfo(name = "email") var email: String? = null
) : Serializable {
#ColumnInfo(name = "id")
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}
It turns out that when using LiveData with room, it immediately returns and so calling LiveData.value immediately gives null while it asynchronously gets the actual data to provide to Observers, and so can't be used as a drop in replacement if trying to convert from a non LiveData call. This is documented here: https://developer.android.com/reference/android/arch/lifecycle/LiveData.html#getValue()
Returns the current value. Note that calling this method on a background thread does not guarantee that the latest value set will be received.