Kotlin Data Class from Json using GSON - java

I have Java POJO class like this:
class Topic {
#SerializedName("id")
long id;
#SerializedName("name")
String name;
}
and I have a Kotlin data class Like this
data class Topic(val id: Long, val name: String)
How to provide the json key to any variables of the kotlin data class like the #SerializedName annotation in java variables ?

Data class:
data class Topic(
#SerializedName("id") val id: Long,
#SerializedName("name") val name: String,
#SerializedName("image") val image: String,
#SerializedName("description") val description: String
)
to JSON:
val gson = Gson()
val json = gson.toJson(topic)
from JSON:
val json = getJson()
val topic = gson.fromJson(json, Topic::class.java)

Based on answer of Anton Golovin
Details
Gson version: 2.8.5
Android Studio 3.1.4
Kotlin version: 1.2.60
Solution
Create any class data and inherit JSONConvertable interface
interface JSONConvertable {
fun toJSON(): String = Gson().toJson(this)
}
inline fun <reified T: JSONConvertable> String.toObject(): T = Gson().fromJson(this, T::class.java)
Usage
Data class
data class User(
#SerializedName("id") val id: Int,
#SerializedName("email") val email: String,
#SerializedName("authentication_token") val authenticationToken: String) : JSONConvertable
From JSON
val json = "..."
val object = json.toObject<User>()
To JSON
val json = object.toJSON()

You can use similar in Kotlin class
class InventoryMoveRequest {
#SerializedName("userEntryStartDate")
#Expose
var userEntryStartDate: String? = null
#SerializedName("userEntryEndDate")
#Expose
var userEntryEndDate: String? = null
#SerializedName("location")
#Expose
var location: Location? = null
#SerializedName("containers")
#Expose
var containers: Containers? = null
}
And also for nested class you can use same like if there is nested object. Just provide Serialize name for the Class.
#Entity(tableName = "location")
class Location {
#SerializedName("rows")
var rows: List<Row>? = null
#SerializedName("totalRows")
var totalRows: Long? = null
}
so if get response from the server each key will map with JOSN.
Alos, convert List to JSON:
val gson = Gson()
val json = gson.toJson(topic)
ndroid convert from JSON to Object:
val json = getJson()
val topic = gson.fromJson(json, Topic::class.java)

Related

Setting a default value in a Kotlin data class conditionally

I am trying to implement an interface in Kotlin, and basically what I am trying to do is say: "If the value atb if null/not set, then set it to bugId".
Here is the Kotlin interface I am trying to implement
interface Incident {
#get:NotNull
#get:Past
val incidentDate: LocalDateTime
#get:NotNull
val source: String
#get:NotNull
#get:Positive
val bugId: Int
#get:NotNull
#get:Pattern(
regexp = "FOO|BAZ",
message = "'\${validatedValue}' not allowed. Must be one of : {regexp}"
)
val pillar: String
#get:Positive
val atb: Int
}
And here is how I am trying to implement it:
data class PSR (
#JsonDeserialize(using = DoubleToInt::class)
override val bugId: Int,
#JsonDeserialize(using = EpochToLocalDateTime::class)
override val incidentDate: LocalDateTime,
override val pillar: String,
#JsonDeserialize(using = DoubleToInt::class)
override val atb: Int = bugId,
) : Incident {
override val source: String = "PSR"
}
The problem is I am trying to deserialize this data from JSON and when I do, I get the following (understandable) error:
Instantiation of [simple type, class com.company.models.spreadsheets.psr.PSR] value failed for JSON property atb due to missing (therefore NULL) value for creator parameter atb which is a non-nullable type
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.company.models.spreadsheets.psr.PSR["atb"])
I've given the equivalent java code below that achieves what I want. How can I do this in Kotlin (and keep the benefits of a data class)?
class PERF implements Incident {
private final LocalDateTime incidentDate;
private final Integer bugId;
private final String pillar;
private final Integer atb;
public PERF(LocalDateTime incidentDate, Integer bugId, String pillar, Integer atb) {
this.bugId = bugId;
this.atb = atb == null ? bugId: atb;
this.incidentDate = incidentDate;
this.pillar = pillar;
}
#NotNull
#Override
public LocalDateTime getIncidentDate() {
return incidentDate;
}
#Override
public int getBugId() {
return bugId;
}
#NotNull
#Override
public String getPillar() {
return pillar;
}
#Override
public int getAtb() {
return atb;
}
#Override
public String getSource() {
return "PERF"
}
}
Assuming you are using Jackson, just create a secondary constructor and mark that as JsonCreator instead. Have the secondary constructor delegate to the primary one.
data class PSR (
override val bugId: Int,
override val incidentDate: LocalDateTime,
override val pillar: String,
override val atb: Int = bugId,
) : Incident {
#JsonCreator
constructor(
#JsonDeserialize(using = DoubleToInt::class)
#JsonProperty("bugId")
bugId: Int,
#JsonDeserialize(using = EpochToLocalDateTime::class)
#JsonProperty("incidentDate")
incidentDate: LocalDateTime,
#JsonProperty("pillar")
pillar: String,
#JsonDeserialize(using = DoubleToInt::class)
#JsonProperty("atb")
atb: Int?,
): this(bugId, incidentDate, pillar, atb ?: bugId)
override val source: String = "PSR"
}

Retrofit #URL does not seem to work with Response<T> as the return type in API

Code that fails
Api class
#GET
fun getBlogPosts(#Url url: String): Response<List<BlogPostModel>>
Calling the api
homeApi.getBlogPosts("hidden-url")
Code that runs
Api class
#GET
fun getBlogPosts(#Url url: String): Call<List<BlogPostModel>>
Calling the api
homeApi.getBlogPosts("redacted-full-url").enqueue(callback)
Why doesn't #URL annotation works with Response?
Error
Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for retrofit2.Response<java.util.List<com.redacted.home.data.dataSources.remote.models.BlogPostModel>>
BlogPostModel class
data class BlogPostModel(
#Json(name = "id") val id: Long = 0,
#Json(name = "title") val title: RenderedModel = RenderedModel(),
#Json(name = "content") val content: RenderedModel = RenderedModel(),
#Json(name = "jetpack_featured_media_url") val featuredImageUrl: String = "",
#Json(name = "date") val date: String = "",
#Json(name = "status") val status: String = "",
#Json(name = "type") val type: String = "",
#Json(name = "excerpt") val excerpt: RenderedModel = RenderedModel(),
#Json(name = "link") val link: String = ""
)
data class RenderedModel(
#Json(name = "rendered") val rendered: String = ""
)
I fixed it by calling execute() instead of Call (2020 stop using callbacks.).
Then i chose the flow to flowOn a IO Dispatcher to overcome main thread issue.

change field name to lowercase while deserializing POJO to JSON using GSON?

I have a POJO class like this. I am deserializing my JSON to below POJO first..
public class Segment implements Serializable {
#SerializedName("Segment_ID")
#Expose
private String segmentID;
#SerializedName("Status")
#Expose
private String status;
#SerializedName("DateTime")
#Expose
private String dateTime;
private final static long serialVersionUID = -1607283459113364249L;
...
...
...
// constructors
// setters
// getters
// toString method
}
Now I am serializing my POJO to a JSON like this using Gson and it works fine:
Gson gson = new GsonBuilder().create();
String json = gson.toJson(user.getSegments());
System.out.println(json);
I get my json printed like this which is good:
[{"Segment_ID":"543211","Status":"1","DateTime":"TueDec2618:47:09UTC2017"},{"Segment_ID":"9998877","Status":"1","DateTime":"TueDec2618:47:09UTC2017"},{"Segment_ID":"121332121","Status":"1","DateTime":"TueDec2618:47:09UTC2017"}]
Now is there any way I can convert "Segment_ID" to all lowercase while deserializing? I mean "Segment_ID" should be "segment_id" and "Status" should be "status". Is this possible to do using gson? So it should print like this instead.
[{"segment_id":"543211","status":"1","datetime":"TueDec2618:47:09UTC2017"},{"segment_id":"9998877","status":"1","datetime":"TueDec2618:47:09UTC2017"},{"segment_id":"121332121","status":"1","datetime":"TueDec2618:47:09UTC2017"}]
if I change the "SerializedName" then while deserializing my JSON to POJO, it doesn't work so not sure if there is any other way.
You need to provide alternative names for deserialisation process and primary (value property) for serialisation.
class Segment {
#SerializedName(value = "segment_id", alternate = {"Segment_ID"})
#Expose
private String segmentID;
#SerializedName(value = "status", alternate = {"Status"})
#Expose
private String status;
#SerializedName(value = "datetime", alternate = {"DateTime"})
#Expose
private String dateTime;
}
Now, you can deserialise fields: Segment_ID, DateTime, Status and still be able to serialise as desired.

How to pull json data using java

I am unable to pull data from this in java. The problem is that how name_value_list will be fetched afterwards. Thanks in advance.
{
"id" : "ets7qkt1luugsj828jugs8vuq5",
"module_name" : "Users",
"name_value_list" : { "mobile_max_list_entries" : { "name" : "mobile_max_list_entries",
"value" : null
},
"mobile_max_subpanel_entries" : { "name" : "mobile_max_subpanel_entries",
"value" : null
},
"user_currency_id" : { "name" : "user_currency_id",
"value" : "-99"
},
.
.
"user_language" : { "name" : "user_language",
"value" : "en_us"
},
"user_name" : { "name" : "user_name",
"value" : "abcd"
},
"user_number_seperator" : { "name" : "user_number_seperator",
"value" : ","
}
}
}
I would say to try using google's Gson() to deserialize.
First, build the entity (or object mapping) classes, something like:
public class MobileMaxListEntry implements Serializable {
#Expose
#SerializedName("name")
private String name;
#Expose
#SerializedName("name")
private String value;
//getters / setters
}
Do this for each entity you have. Then:
public class ValuesList implements Serializable {
#Expose
#SerializedName("mobile_max_list_entries")
private MobileMaxListEntry mobile_max_list_entries;
#Expose
#SerializedName("mobile_max_subpanel_entries")
private MobileMaxSubpanelEntry mobile_max_subpanel_entries;
//getters/setters for everything
}
Then, the response class:
public class MyResponseClass implements Serializable {
#Expose
#SerializedName("id")
private String objectId;
#Expose
#SerializedName("module_name")
private String moduleName;
#Expose
#SerializedName("name_value_list")
private ValuesList valuesList;
//getters / setters for everything
}
Then, finally, in your code:
Gson gson = new Gson();
String json = "your_json_here";
MyResponseClass deserialized = gson.fromJson(json, MyResponseClass.class);
If you get an error, try adding default empty constructors for each class.
Simply extract the data in Map<String,Object> from JSON string.
GSON library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
System.out.println(data.get("name_value_list"));
Jackson library
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
Map<String, Object> data = mapper.readValue(reader, typeRef);
System.out.println(data.get("name_value_list"));
You can create a POJO class that is exact replica of the JSON string and can convert it in the Java object using GSON.
sample code:
class MobileDetail{
private NameValueList name_value_list;
private String module_name;
private String id;
}
class NameValueList{
private NameValue mobile_max_list_entries;
private NameValue mobile_max_subpanel_entries;
private NameValue user_currency_id;
private NameValue user_language;
private NameValue user_name;
private NameValue user_number_seperator;
}
class NameValue{
private String name;
private String value;
}
...
MobileDetail data =new Gson().fromJson(reader, MobileDetail.class);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));

How to parse dynamic JSON fields with GSON?

So I'm using GSON to parse JSON from an API and am stuck as to how to have it parse the dynamic fields in the data.
Here is an example of the JSON data returned on a query:
{
-
30655845: {
id: "30655845"
name: "testdata
description: ""
latitude: "38"
longitude: "-122"
altitude: "0"
thumbnailURL: http://someimage.com/url.jpg
distance: 9566.6344386665
}
-
28688744: {
id: "28688744"
name: "testdata2"
description: ""
latitude: "38"
longitude: "-122"
altitude: "0"
thumbnailURL: http://someimage.com/url.jpg
distance: 9563.8328713012
}
}
The way I am currently handling the single static values is with a class:
import com.google.gson.annotations.SerializedName;
public class Result
{
#SerializedName("id")
public int id;
#SerializedName("name")
public String name;
#SerializedName("description")
public String description;
#SerializedName("latitude")
public Double latitude;
#SerializedName("longitude")
public Double longitude;
#SerializedName("altitude")
public Double altitude;
#SerializedName("thumbnailURL")
public String thumbnailURL;
#SerializedName("distance")
public Double distance;
}
And then I can simply use GSON to parse that:
Gson gson = new Gson();
Reader reader = new InputStreamReader(source);
Result response= gson.fromJson(reader, Result.class);
I know this works on the sub-data as I can query and get a single entry and parse that quite easily, but what about the random integer values given for each value in the array? (ie the 30655845 and 2868874)
Any help?
According to GSON documentation you can do things like:
Type mapType = new TypeToken<Map<Integer, Result> >() {}.getType(); // define generic type
Map<Integer, Result> result= gson.fromJson(new InputStreamReader(source), mapType);
Or you can try to write custom serializer for your class.
Disclaimer: I too, have no experience with GSon but with other frameworks like Jackson.

Categories