Map java Enum to Kotlin data class - java

I am making a call from service A which is in Kotlin to service B which is in Java. It return me an object which contains multiple fields. One of the fields returned in the Java object is an enum. In my kotlin code I have defined a DTO which maps the returned response to kotlin. I need to map this enum to a string value in kotlin.
DTO in Java:
public class PersonDTO
{
private Long id;
private String name;
private CountryCode countryCode;
}
The CountryCode is an enum.
Data class in Kotlin:
data class PersonDTO(
val id: Long? = null,
val name: String? = null,
val countryCode: String? = null //How to map the enum to string here..???
)
Any help would be appreciated.

Answer to the edited question: How to map a Java enum to a String?
you can call name() or toString() on an enum to get a String representation of it.
name() cannot be overwritten and always returns the textual representation of the value defined in the code, while toString() can be overwritten, so it might be depending on your use case what to use. Because of the fact that name() cannot be overwritten I prefer to always use name() wich can have less side effects or unexpected behavior when working with libraries which are not under your control.
Original Answer:
1 you don't have to do this. You can use the same Java class also in Kotlin code.
2 You could just reuse the enum, like in option 1) you can reuse the Java enum in Kotlin code:
data class PersonDTO(
val id: Long? = null,
val name: String? = null,
val countryCode: CountryCode
)
3 You can write a Kotlin enum with a mapping function to create the matching instance of the enum:
enum class KotlinCountryCode {
EXAMPLE;
fun fromJavaCountryCode(input: CountryCode): KotlinCountryCode? {
if (input.name() == EXAMPLE.name) {
return EXAMPLE
}
return null
}
}

Related

Type mismatch, when passing arugment to Model Class

The problem is when I created the instance of my Model class and pass the non-nullable variable to the constructor, the compiler shows the error Type-mismatch.
I have fixed the type-mismatch error by making model class variable as nullable
But I couldn't understand the error shown by the compiler.
Model class
class SharedPreferenceEntry (val name:String, val dateOfBirth:Calendar, val email:String)
Helper class SharedPreferencesHelper, where I created an instance of Model class and return that instance from function
fun getPersonalInfo(): SharedPreferenceEntry { // Get data from the SharedPreferences.
val name = mSharedPreferences.getString(KEY_NAME, "")
val dobMillis =
mSharedPreferences.getLong(KEY_DOB, Calendar.getInstance().getTimeInMillis())
val dateOfBirth: Calendar = Calendar.getInstance()
dateOfBirth.setTimeInMillis(dobMillis)
val email = mSharedPreferences.getString(KEY_EMAIL, "")
// Create and fill a SharedPreferenceEntry model object.
return SharedPreferenceEntry(name, dateOfBirth, email)
}
As #sonnet commented, the use of mSharedPreferences.getString(...) will return null if the key is mapped to null. To ensure, the value of mSharedPreferences.getString(...) is non-null, change it to mSharedPreferences.getString(...) ?: "".

How to use PolymorphicJsonAdapterFactory with an interface in Moshi?

I have an interface with two concrete types as part of my model that I'd like to serialize/deserialize with Moshi. My issue is that I don't fully understand if PolymorphicJsonAdapterFactory is actually meant for my use case. I've looked at the samples and a few blog posts and (if I'm understanding them correctly) all of them seem to point to the fact that your interface is supposed to have a field in the interface that allows you to determine the type. I'm working in an existing codebase and so I can't easily add a field that would allow me to figure out what type it is by some string literal.
This is where I'm at with my Moshi code and I'm seeking validation on whether or not I'm using PolymorphicJsonAdapterFactory correctly. Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
String json = ...;
Moshi moshi = new Moshi.Builder()
.add(PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
)
.build();
JsonAdapter<MyModel> jsonAdapter = moshi.adapter(MyModel.class);
MyModel myModel = jsonAdapter.fromJson(json);
Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
MyModel is defined as the following
class MyModel {
String month;
PersonInterface person;
}
My interface and concrete classes in kotlin:
Interface PersonInterface {
val personsName: String?
}
data class BusinessPerson(
override val personsName: String,
val occupation: String?
) : PersonInterface
data class PolicePerson(
override val personsName: String,
val rank: String?
) : PersonInterface
The goal is to hopefully have Moshi be able to create a
class MyModel {
String month;
BusinessPerson person;
}
or a
class MyModel {
String month;
PolicePerson person;
}
depending on whether or not the person field contains an occupation (which means it's of type BusinessPerson) or if it contains the field rank (which means it's of type PolicePerson).
PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
means that JSON for every person is supposed to contain a key "" and "occupation" or "rank" is the value for this key, not a key name itself as you want. So e.g. it would encode a
BusinessPerson("John", "CEO")
as
{"": "occupation", "personsName": "John", "occupation": "CEO"}
I think you'll have to write your own adapter factory for this use-case if you want to avoid a discriminator field.

Populating a Kotlin Data Class using Gson

I am attempting to populate a Kotlin data class from a corresponding string value. I looked at: Kotlin Data Class from Json using GSON but what I am attempting to do is not tracking exactly the same:
fun convertStringToObject(stringValue: String?, clazz: Class<*>): Any? {
return if (stringValue != null) {
try {
val gson = Gson()
gson.fromJson<Any>(stringValue, clazz)
}
catch (exception: Exception) {
// yes, we are swallowing the possible
// java.lang.IllegalStateException
null
}
}
else {
null
}
}
Calling this function and attempting to populate the following class:
data class LoggedUser(
#SerializedName("id") val id: Long,
#SerializedName("name") val name: String,
#SerializedName("first_name") val firstName: String,
#SerializedName("last_name") val lastName: String,
#SerializedName("email") val email: String
)
It executes but LoggedUser values are empty (null).
The stringValue is currently:
{"nameValuePairs":{"id":"1654488452866661","name":"Bob Smith","email":"bob.smith#test.net","first_name":"Bob","last_name":"Smith"}}
I even tried using a hardcoded class value like this:
gson.fromJson(stringValue, LoggedUser::class.java)
but there was no joy. The stringValue is what gson.toJson(value) generated where value was a JSONObject.
Anybody have an idea what my disconnect is?
Your JSON string actually has two JSON objects, the outer value (which has a key called nameValuePairs) and the value you actually want to deserialize (which is the value at key nameValuePairs). You need to dive one level deeper, either through an outer wrapper class which holds your User object or by manually getting the JsonObject at key nameValuePairs as a String and then passing that to Gson. If the string was just {"id":"1654488452866661","name":"Bob Smith","email":"bob.smith#test.net","first_name":"Bob","last_name":"Smith"} it would deserialize fine.
Create a wrapper class
data class LoggedUserWrapper{
#SerializedName("nameValuePairs") val nameValuePairs: LoggedUser
}
and execute
val loggedUser = convertStringToObject(yourJsonString, LoggedUserWrapper::class.java)
This will help you.

Kotlin data class implementing Java interface

I'm trying to introduce Kotlin into my current project. I've decided to begin with entities, which seem to map perfectly to data classes.
For example I have a data class:
data class Video(val id: Long, val ownerId: Long, val title: String, val description: String? = null,
val imgLink: String? = null, val created: Date? = null, val accessKey: String? = null,
val views: Long? = null, val comments: Long? = null, val videoLink: String? = null): Entity
Which implements Java interface:
public interface Entity {
Long getId();
}
But for some reason compiler doesn't understand that method is implemented already:
Class 'Video' must be declared abstract or implement abstract member public abstract fun getId(): kotlin.Long! defined in net.alfad.data.Entity
Do I have to use any additional keywords for id param? What does "!" mean in the signature?
The problem here is that Kotlin loads the Java class Entity first and it sees getId as a function, not as a getter of some property. A property getter in a Kotlin class cannot override a function, so the property id is not bound as an implementation of the getId function.
To workaround this, you should override the original function getId in your Kotlin class. Doing so will result in JVM signature clash between your new function and id's getter in the bytecode, so you should also prevent the compiler from generating the getter by making the property private:
data class Video(
private val id: Long,
...
): Entity {
override fun getId() = id
...
}
Note that this answer has been adapted from here: https://stackoverflow.com/a/32971284/288456
If this is your whole data class then you're not overriding getId(). I see that you have a property called id and Kotlin should generate a getter for that but that won't be marked with the override keyword which you need to indicate that you're overriding an abstract function.
-- EDIT --
Alexander beat me to it! His answer is better anyway! ;)

take the value of enum and covert it to String

I should take from a variable enum its value and transform it to string.how can i do?
here it is the type enum:
public enum State{
b,c,p;
};
now i have to insert into an object String one value.
You might use enum.name orenum.toString to get the name of the enum constant, or enum.ordinal to get the ordinal position.
you can use name() or toString(), so :
State aState = State.c;
String strState = aState.name();
See here the official java reference for more information...
State.b.toString() will return "b". The same goes for the other ones.
Usually,
State state = ...;
String string = state.toString();
should work, but it is not recommended since someone might override toString for some other purpose.
Instead the method you are looking for is
String string = state.name();
As an aside, your enumerated stated should always be all in capitals, and they should have descriptive names. It's not a language rule, but a convention. For example enum State { ON, OFF, PAUSED; }.
I tend to do something more complicated, but I find that it's more flexible:
public enum MyEnumeration {
SOME_NAME("Some Name"),
OTHER_THING("Other Thing"),
...
MORE_VALUES("More Values"),
private final String displayName;
private MyEnumeration(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
This way, I use standard capitalization for my enums in code, but can have a more presentable name for them.
This trick can also be used to replace ordinal, by initializing a number, and then you don't need to worry about rearranging your enums.
Method #1: Using the built-in toString() and name() methods
If you want to print a String that is the same as the value of the State, then you can use the toString() method, or the name() method.
System.out.println(State.b); // Prints "b"
System.out.println(State.c); // Prints "c"
System.out.println(State.p); // Prints "p"
Method #2: Using a constructor to create a custom mapping
If you want to have a custom String associated with each of those states, you can use a constructor to associate a particular value with each enum value:
public enum State{
b("State B"), c("State C"), p("State P");
private String longName;
private State(String longName) {
this.longName = longName;
}
#Override
public String toString() {
return this.longName;
}
};
Of course, if you don't want to break the default toString() usage, you can create a different method called getFullName(), for example, to return the custom value.

Categories

Resources