How to use GSON to deserialize <Range<Date>> object - date

My problem is simple. I have to implement some TypeConverters to manage data for a Room database in Android.
I have no problem to serielize a Range< Date>> to a string for storage it. I'm using the easy way:
#TypeConverter
fun rangeToString(range: Range <Date>?): String {
val gson = Gson()
return gson.toJson(range)
}
But when I try to get back the same information with the reverse process:
#TypeConverter
fun stringToRange(value: String): Range <Date>? {
val mapType = object: TypeToken<Range <Date>?>() {}.type
return Gson().fromJson (value, mapType)
}
I'm getting an error: Unable to invoke no-args constructor for java.util.Comparator <java.util.Date>. Registering an InstanceCreator with Gson for this type may fix this problem.
I understand that the problem is GSON doesn't know how to instance a Range with Dates. I think that maybe if I use a custom registerTypeAdapter or something similar could solve the problem but I'm not finding the way.
Any idea? Thank you!

Let's make it easier. I could not figure out how to deserialize a Range< Date> but I've solved it changing the object for a Pair<Date, Date>.
My new final and easy TypeConverters:
#TypeConverter
fun stringToRange(value: String): Pair<Date, Date>? {
val mapType = object : TypeToken<Pair<Date, Date>?>() {}.type
return Gson().fromJson(value, mapType)
}
#TypeConverter
fun rangeToString(range: Pair<Date, Date>?): String {
val gson = Gson()
return gson.toJson(range)
}
You can easily transform your Range< Date> doing:
Pair(dateRange.minimum, dateRange.maximum)
With this solution I'm not answering my question but this is another way to fix the problem.

Related

How to change the code as more Scala-like one regarding in the point of resource close?

Below is the scala code that use s3 client and open the s3 bucket and read the object.
def readS3(
region: String,
bucket: String,
key: String,
fromLine: Int,
toLine: Int,
lineSeparator: String,
): String = {
val client = S3Client.builder()
.region(Region.of(region))
.build()
val objectRequest = GetObjectRequest.builder()
.key(key)
.bucket(bucket)
.build()
val objectBytes = client.getObjectAsBytes(objectRequest)
val in = objectBytes.asInputStream()
val bufferedReader = new BufferedReader(new InputStreamReader(in))
try {
val sb = readLine(fromLine, toLine, 1, lineSeparator, bufferedReader, new mutable.StringBuilder)
sb.toString
} finally {
in.close()
bufferedReader.close()
client.close()
}
}
As you can see, I tried to close all the resource-related objects in the finally clause.
However, I think that I can change the code as more Scala-like version
because it seems to be Java-like as I think.
I think that in the code above instead of creating s3 client object every time, I am able to use lazy keyword to prevent creating every s3 client object.
And also, I didn't like the way that I use the close in the finally clause.
Instead of closing the resourcing in the finally, is there any other way to achieve my goal?
Note that I am using the Scala version 2.12, so I cannot use command for resource management.
Thanks.

Instanciating POJO with nulls values from Scala Option

Many years of using Scala and still I don't know the right way to interoperate with Java. :(
To be honest, this is not something that I'm doing every day, but sometimes there is no other option (today is Firestore Java libraries).
My question today is about the proper way to instantiate POJOs that can have null values.
At the end of the day, I always use something like the def toJavaLong(l: Option[Long]): java.lang.Long = if (l.isEmpty) l.get else null, but pretty sure that there is a better and elegant way.
Please, could you show me the path? I expect something like orNull working out of the box, but it is never the case.
I'm using Scala 2.13, but feel free to show alternatives to Scala 3 as well.
In the next example, I explain the errors that I have using orNull and getOrElse:
Pojo:
public class ExamplePojo {
public Long value;
public ExamplePojo(Long value) {
this.value = value;
}
}
Scala object instanciating the Pojo:
object CreatePojo {
def main(args: Array[String]): Unit = {
val noneValue = Option.empty[Long]
val oneValue = Some(1L)
def toJavaLong(l: Option[Long]): java.lang.Long =
if (l.isEmpty) l.get else null
// Next line works fine.
new ExamplePojo(toJavaLong(noneValue))
// Next line works fine of course, but not helpful. :)
new ExamplePojo(oneValue.get)
// Next line throws a compilation error:
// type arguments [Long] do not conform to method orNull's type parameter bounds [A1 >: Long]
new ExamplePojo(noneValue.orNull)
// Next line throws a compilation error:
// type mismatch;
// found : Any
// required: Long
new ExamplePojo(noneValue.getOrElse(null))
// This will throw a `java.util.NoSuchElementException`
new ExamplePojo(noneValue.get)
}
}
Of course, this is only an example to reproduce the case.
The problem, in this case, is not the null, but the fact that scala.Long is not the same as java.lang.Long
What you can do is the following:
new ExamplePojo(l.map(Long.box).orNull)
Likely not as concise as you'd expect but I tend to use the following:
option.getOrElse(null).asInstanceOf[java.lang.Long]
Or write an extension method asJavaOrNull that does what your toJavaLong currently do but with a nicer developer experience.

API return writeable

I'm trying to convert a few endpoints I have to use concurrency. I have the following method for the controller:
Original method
def getHistory(id:String) = Action {
val response = historyService.getPersonHistory(id)
Ok(write(response)) as "application/json"
}
New Method
def getHistory(id:String) = Action.async {
val response = scala.concurrent.Future {
historyService.getPersonHistory(id)
}
response.map(i => Ok(i))
}
So, when we try this with a simple example process (not calling another method, but just calculating an Int) it seems to work. In the new version above, I keep getting the error:
"No implicits found for parameter writable: Writeable[historyResponse]
Cannot write an instance of models.HistoryResponse to HTTP response. Try to define a Writeable[models.HistoryResponse]"
I'm new to Scala, and having difficulty finding information on making writeables. What do I need to be able to return the results as before?
Thanks
You need to define an implicit val tjs: Writes[HistoryResponse] or even better, implicit val format: Format[HistoryResponse] = Json.format[HistoryResponse] in the companion object for HistoryResponse, so that play can auto convert your data to json. by the way, not a good name for i in the map function, something like "history" would be better instead of "i".

What's the best way to get an object from Java Enumeration without code loops?

What's the best way to get an object from Java Enumeration using Scala? I'm retrieving this value using a while loop but i would like to use something like .map() and get this without need to code a loop:
private def getMsgId(mimeMessage: MimeMessage): String = {
var msgId: String = null
val iterator = enumerationAsScalaIterator(mimeMessage.getAllHeaders())
breakable {
while (iterator.hasNext) {
val header = iterator.next.asInstanceOf[Header]
if (header.getName().equalsIgnoreCase("MessageId")) {
msgId = header.getValue()
break
}
}
}
return msgId;
}
What's is the another options to get value "header.getValue()" without using loops like while or for?
Thank in advance.
One note: if your function might actually not find a message ID you're looking for, it should return Option[String] instead.
Having said that, there already is an implicit conversion from Enumeration[A] to Scala's Iterator[A], you just have to:
import collection.JavaConversions.enumerationAsScalaIterator
and after that:
mimeMessage.getAllHeaders
.find(_.getName.equalsIgnoreCase("MessageId")
.map(_.getValue)
which will return Option[String].

Implementing a java interface in Scala

I have the following code for building a cache using google collections:
val cache = new MapMaker().softValues().expiration(30,
TimeUnit.DAYS).makeComputingMap(
new com.google.common.base.Function[String,Int] {
def apply(key:String):Int ={
1
}
})
And I am getting the following error message:
error: type mismatch;
found : java.lang.Object with
com.google.common.base.Function[java.lang.String,Int]{ ... }
required: com.google.common.base.Function[?, ?]
new com.google.common.base.Function[String,Int] {
^
I am wondering why the types don't match ?
The actual code is:
import com.google.common.collect.MapMaker
trait DataCache[V] {
private val cache = new MapMaker().softValues().makeComputingMap(
new com.google.common.base.Function[String,V] {
def apply(key:String):V = null.asInstanceOf[V]
})
def get(key:String):V = cache.get(key)
}
Kind regards,
Ali
PS - I am using google-collections v1
You need to supply type parameters to the final method call. You are going through the raw type interface and scala cannot reconstruct the type information.
val cache = new MapMaker().softValues().expiration(30,
TimeUnit.DAYS).makeComputingMap[String, Int](
new com.google.common.base.Function[String,Int] {
def apply(key:String):Int ={
1
}
})
Does the following works?
new com.google.common.base.Function[_,_] {
If that doesn't work, you may wish to keep the declaration as it is right now, and then add a : com.google.common.base.Function[_, _] after it, like this:
val cache = new MapMaker().softValues().expiration(30,
TimeUnit.DAYS).makeComputingMap(
new com.google.common.base.Function[String,Int] {
def apply(key:String):Int ={
1
}
}: com.google.common.base.Function[_, _])
I have heard that some Google stuff use raw types, which are rather hard to integrate well with Scala. And, in fact, should be banished back to hell, where they came from, but that's just imho.
Also, if you could compile that with -explaintypes, we may get a better notion of what is failing.