Can I access enum property with string in kotlin? - kotlin

I have an enum of regions like this:
enum class Regions(val location:String){
REGION_1("London"),
}
Is there a way to access the properties of region_1 with just a string like in the below function?
fun access(name:String){
return Regions.<modifed_name>.location
}

You can convert your string to enum using valueOf(value: string) and then use the location
fun access(name:String): String = Regions.valueOf(name.uppercase()).location

enum class Regions(val location: String) {
REGION_1("London"),
REGION_2("Berlin"),
REGION_3("Pairs")
}
fun access(name:String): String {
return Regions.values().firstOrNull() { it.name == name }?.location ?: "location not found"
}
println(access("REGION")) // Output: location not found
println(access("REGION_2")) // Output: Berlin

Related

Testing a private method in Kotlin

I'm trying to test a private method which takes the below parameters as input :
ClassToBeTested
delete(
someId1: String,
aList: List<CustomObject>,
someId2: String,
someId3: String,
someId4: String
) : Long? {
}
TestClass
val method = ClassToBeTested.javaClass.getDeclaredMethod("delete", String::class.java,
List::class.java, String::class.java, String::class.java, String::class.java)
method.isAccessible = true
val testId = method.invoke(ClassToBeTested, someId1, aList, someId2, someId3, someId4)
I end up getting the below error :
java.lang.NoSuchMethodException:
ClassToBeTested$Companion.delete(java.lang.String, java.util.Arrays$ArrayList, java.lang.String, java.lang.String, java.lang.String)
When I tried changing the above method declaration as :
val method = ClassToBeTested.javaClass.getDeclaredMethod("delete", String::class.java,
List<CustomObject>::class.java, String::class.java, String::class.java, String::class.java)
I get the below error :
Kotlin: Only classes are allowed on the left hand side of a class literal
Is there anyway we can test the private function which takes a parameter of List of Custom Objects?
Don't use reflection for this. Simply make the method internal and call it directly from your tests. You can even add an annotation like #VisibleForTesting or #TestOnly, those are available in various libraries.
You can use Kotlin's declaredMemberFunctions property to find it based on its name alone:
data class CustomObject(val id: String)
class ClassToBeTested {
private fun delete(
someId1: String,
aList: List<CustomObject>,
someId2: String,
someId3: String,
someId4: String
): Long? {
return someId1.toLongOrNull()
}
}
class SampleTest {
#Test
fun test1() {
val instance = ClassToBeTested()
val method = instance::class.declaredMemberFunctions.first { it.name == "delete" }.apply { isAccessible = true }
val result = method.call(instance, "1", emptyList<CustomObject>(), "2", "3", "4")
assertEquals(1L, result)
}
}

Function to return the value of a Map by key in an enum

I have an enum class which maps language locale to a list.
How can I fix the function getReservationFrequencies inside the companion object
to return the value of the map based on the locale (key)?
enum class ReservationFrequencies(val frequencies: Map<Language, List<ReservationFrequency>>) {
APPLICATION(mapOf(
Language.en to listOf(
ReservationFrequency(0,"once",0),
ReservationFrequency(1,"Monthly",2),
ReservationFrequency(2,"Quarterly",5),
ReservationFrequency(3,"Semi-annually",5),
ReservationFrequency(4,"Annually",5)
),
Language.ar to listOf(
ReservationFrequency(0,"مرة واحده",0),
ReservationFrequency(1,"شهرياً",2),
ReservationFrequency(2,"ربع سنوياً",5),
ReservationFrequency(3,"نصف سنوياً",5),
ReservationFrequency(4," سنوياً",5)
)
));
}
I tried creating a companion object that includes a function that returns the list
companion object {
fun getReservationFrequencies(locale: String?) : List<ReservationFrequency> {
val reservationFrequencyDtos = mutableListOf<ReservationFrequency>()
reservationFrequencies.values()
.filter { locale!!.contains(it.name)}
.forEach() {
ReservationFrequency.add(ReservationFrequency(code = it.frequencies))
}
}
}
I'm expecting a List<ReservationFrequency> based on the locale
Probably just this
fun getReservationFrequencies(locale: String?) : List<ReservationFrequency> =
ReservationFrequencies.APPLICATION.frequencies.filter {
it.key.name == locale // I don't know the specifics of how to match the String locale arg against Language
}.map {
it.value
}.first()

Serialize enum field into JSON in Kotlin

I've got a stupid question that stunned me a bit.
I have an enum and a data class like this:
enum class MyEventType(val typeName: String) {
FIRST("firstEventReceived")
}
data class MyEvent(
val id: String,
val event: MyEventType
)
I need to send this as a json string, but common desearilizer makes such a json
{
"id": "identifier",
"event": "FIRST"
}
but i need
{
"id": "identifier",
"event": "firstEventReceived"
}
As far as i understand, kotlin allows to override getter in data class, but i didn't succeed in it... Trying to make
data class MyEvent(
val id: String
) {
val event: MyEventType get() event.typeName
}
but i've missed something, i guess...
The simplest way is probably to annotate the property with #JsonValue:
enum class MyEventType(#JsonValue val typeName: String) {
FIRST("firstEventReceived")
}
data class MyEvent(
val id: String,
val event: MyEventType
)
fun main() {
MyEvent(id = "foo", event = MyEventType.FIRST)
.let { jacksonObjectMapper().writeValueAsString(it) }
.let { println(it) }
}
Prints:
{"id":"foo","event":"firstEventReceived"}
The easiest way is to annotate the typeName with #JsonValue. This will serialise and deserialise the enum field as you want.
enum class MyEventType(#JsonValue val typeName: String) {
FIRST("firstEventReceived");
}
An alternative is to use #JsonFormat (if you are using jackson version < 2.9);
enum class MyEventType(#JsonFormat(shape = JsonFormat.Shape.OBJECT) val typeName: String) {
FIRST("firstEventReceived");
}
Herer's an example;
#JvmStatic
fun main(args: Array<String>) {
val mapper = jacksonObjectMapper()
val json = mapper.writeValueAsString(MyEvent("1", MyEventType.FIRST))
println(json)
val event = mapper.readValue<MyEvent>(json)
println(event)
}
You get the output;
{"id":"1","event":"firstEventReceived"}
MyEvent(id=1, event=FIRST)
I used Jackson version 2.12.0. Here's a good read on enum manipulation with Jackson - https://www.baeldung.com/jackson-serialize-enums
Also you can have enum with 2+ fields which you want to be serialized
enum class MyEventType(
val firstField: String,
val secondField: String,
val thirdField: String
) {
MY_ENUM("firstFieldValue", "secondFieldValue", "thirdFieldValue")
}
You can chose one of the following two options:
Put #JsonValue over a method(lets call it getter) that will return the required value(if you need only part of the fields):
#JsonValue
fun getSerializedObject(): String {
return "{firstField: $firstField, thirdField: $thirdField}"
}
Result will be "{firstField: firstFieldValue, thirdField: thirdFieldValue}"
Put #JsonFormat(shape = JsonFormat.Shape.OBJECT) over your enum class(for serialization class as common class):
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum class MyEventType(
val firstField: String,
val secondField: String,
val thirdField: String
) {
MY_ENUM("firstField", "secondField", "thirdField")
}
Result will be "{"firstField": "firstFieldValue", "secondField": "secondFieldValue", "thirdField": "thirdFieldValue"}"
For GSON users, you can use the #SerializedName annotation:
enum class ConnectionStatus {
#SerializedName("open")
OPEN,
#SerializedName("connecting")
CONNECTING,
#SerializedName("closed")
CLOSED
}

Convert enum member to corresponding text

In Kotlin I have an enum as follows:
enum class MediaType() {
AUDIO,
VIDEO,
ARTICLE;
}
I would like to add either a function or some property that allows an enum member to be converted to some corresponding text. For example:
var mediaType = MediaType.AUDIO
var text = mediaType.toText() // returns the string "MP3"
mediaType = MediaType.VIDEO
text = mediaType.toText() // returns the string "mpeg"
While I can add the toText function to the MediaType class, I am not sure how that function references the value it is set to.
You can add a property to the enum...
enum class MediaType(val text: String) {
AUDIO("mp3"),
VIDEO("mpeg"),
ARTICLE("text");
}
And then use it like this:
println(MediaType.AUDIO.text)
If you'd like a toText() function rather than a property, that can be added as well, but probably isn't as idiomatic:
enum class MediaType(private val text: String) {
AUDIO("mp3"),
VIDEO("mpeg"),
ARTICLE("text");
fun toText(): String = text
}
Update:
Another way is to add an extension function and keep this logic outside the enum entirely:
fun MediaType.toText(): String =
when(this) {
MediaType.AUDIO -> "mp3"
MediaType.VIDEO -> "mpeg"
MediaType.ARTICLE -> "text"
}
enum class MediaType() {
AUDIO,
VIDEO,
ARTICLE;
fun getMemberText() = when (this) {
AUDIO -> "mp3"
VIDEO -> "mpeg"
else -> "text"
}
}

Kotlin: How to check if enum contains a given String without messing with Exceptions?

Why there is no contains method in enum classes (as well as in Java)? And how to implement it elegantly? Now I'm using this ugly approach:
val contains: Boolean =
try {
MyEnum.valueOf("some string")
true
} catch (e: IllegalArgumentException) {
false
}
enumContains
You can create an enumContains function similar to Hound Dog's answer but using reified type parameters instead.
You cannot create a JVM independent solution in Kotlin 1.0 but you can in Kotlin 1.1 using enumValues.
Kotlin 1.1
/**
* Returns `true` if enum T contains an entry with the specified name.
*/
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
return enumValues<T>().any { it.name == name}
}
Kotlin 1.0
/**
* Returns `true` if enum T contains an entry with the specified name.
*/
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
return T::class.java.enumConstants.any { it.name == name}
}
Usage
enumContains<MyEnum>("some string") // returns true or false
enumValueOfOrNull
If you also need the actual enum entry then you might consider creating an enumValueOfOrNull function instead.
Kotlin 1.1
/**
* Returns an enum entry with the specified name or `null` if no such entry was found.
*/
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
return enumValues<T>().find { it.name == name }
}
Kotlin 1.0
/**
* Returns an enum entry with the specified name or `null` if no such entry was found.
*/
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
return T::class.java.enumConstants.find { it.name == name }
}
Usage
enumValueOfOrNull<MyEnum>("some string")
You can just take values Array of your enum and use contains over it. For example:
Planets.values().map { it.name }.contains("EARTH")
But for this you need to have correct string value so you might uppercase it before search.
If you want to find enum by its value take a look at the Reverse Lookup for enums.
Edit:
As #JamesBassett suggested you can optimize it to stop looking once it finds a match.
Planets.values().any { it.name == "EARTH" }
You could do something like this:
fun <T : Enum<*>> KClass<T>.contains(value: String): Boolean {
return this.java.enumConstants.any { it.name == value }
}
MyEnum::class.contains("some string")
or if you are really adventurous
enum MyEnum {
MY_1, MY_2;
companion object {
fun isInEnum(someString: String) {
return try {
valueOf(someString)
true
} catch (e: Exception) {
false
}
}
}
}