How to convert a Data Class to ByteBuffer in Kotlin? - kotlin

I am trying to use Kinesis, which expects data in byte buffer format. All the examples I have seen so far are in Java and pass simple strings.
Can anybody give an idea of how to convert a kotlin data class to bytebuffer?
e.g.
data class abc (
var a: Long,
var b: String,
var c: Double
)

Check the below method
fun toByteArray(): ByteArray? {
val size: Int = 8 + 8 + string.Size
val byteBuffer = ByteBuffer.allocate(size)
.put(long) //long veriable
.put(double) // double veriable
.put(string)
return byteBuffer.array()
}
You can allocate the size based on dataType size like Int 4 bytes, Double and Long 8 bytes
for reading back to dataType
val byteBuffer = ByteBuffer.wrap(byteArray)
byteBuffer.get(Int) //Int variable
byteBuffer.get(Double) //Double variable
byteBuffer.get(nonce)

You might want to have a look at kotlinx.serialization. It is an official Kotlin project and supports several formats out-of-the-box. You can use the output and wrap it in with ByteBuffer.wrap

Thanks for all the suggestions.
Solved the problem using ObjectMapper() of Jackson library (jackson-databind) and annotations.
Following code used for serialization:
val objectMapper = ObjectMapper()
objectMapper.registerModule(JavaTimeModule())
val buf = ByteBuffer.wrap(objectMapper.writeValueAsString(className).toByteArray(Charsets.UTF_8))
code for deserialization:
val objectMapper = ObjectMapper()
objectMapper.registerModule(JavaTimeModule())
val obj = objectMapper.readValue(Charsets.UTF_8.decode(record.data()).toString(), ClassName::class.java)
Apart from this, I had to add constructors of all the data classes and had to add the following annotation to all the LocalDateTime attributes:
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonFormat(pattern = "YYYY-MM-dd HH:mm")
var edd: LocalDateTime?,

A simple solution with no additional libraries
Note: must be tailored for each data class
data class TimerConfig(val startTime: Long, val repeatCount: Int, val sequenceDuration: Int)
Converting the data class to a ByteArray
private fun TimerConfig.toByteArray(): ByteArray {
val byteBuffer = ByteBuffer.allocate(Long.SIZE_BYTES + Int.SIZE_BYTES + Int.SIZE_BYTES)
byteBuffer.putLong(this.startTime)
byteBuffer.putInt(this.repeatCount)
byteBuffer.putInt(this.sequenceDuration)
return byteBuffer.array()
}
Recovering the data class from the received ByteArray
private fun ByteArray.toTimerConfig(): TimerConfig {
val byteBuffer = ByteBuffer.wrap(this)
return TimerConfig(byteBuffer.long, byteBuffer.int, byteBuffer.int)
}

Related

Why does Moshi parse integers, longs as Double?

I'm trying to parse a not very well designed api's json using Moshi + kotlin. For some reasons it parses numbers like 71 as Double.
The 3rd party api has a list of objects that could either look like:
{"foo":[[1234567000,12]]} // long, int
or
{"foo":[[1234567000,"string",0,2]]} // long, string, int, int
Because of the 3rd party api I have the following kotlin class:
#JsonClass(generateAdapter = true)
class D {
var foo: List<Any> // I use Any because it can be either String or Int or Long
}
and in my code I do something like:
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(D::class.java)
var D d = adapter.fromJson("{\"foo\":[[1234567000,\"string\",0,2]]}")
var index = d.foo[2]
var value : Long = 0
// here I get an error: ClassCastException: java.lang.Double cannot be cast to java.lang.Long
value = d.foo[index]
but for some reason Moshi converts the integers in the json string into Doubles instead of Int or Long. How could I fix it?
I'm not sure if this is the easiest way but it works:
class AnyAdapter {
#FromJson fun fromJson(str: String): Any {
var any: Any
try {
any = Integer.parseInt(str)
} catch (e: NumberFormatException) {
try {
any = java.lang.Long.parseLong(str)
} catch (e: NumberFormatException) {
try {
any = java.lang.Double.parseDouble(str)
} catch (e: NumberFormatException) {
any = str
}
}
}
return any
}
}
val moshi = Moshi.Builder()
.add(AnyAdapter())
.build()
val adapter = moshi.adapter(D::class.java)
var D d = adapter.fromJson("{\"foo\":[[1234567000,\"string\",0,2.0]]}")
var l : Long = d.foo[0] as Long
var s : String = d.foo[1] as String
var i : Int = d.foo[2] as Int
var dd : Double = d.foo[3] as Double
JSON number type makes no distinction between integer and floating-point
Fundamental idea behind any JSON parsing library is to parse JSON into certain type, if that type has properties of type integer then parsing library will try to convert JSON number type to integer, but you are parsing json to Any, which essentially tells moshi to take a guess as to the type of the Object.
Since JSON doesn't distinguish between integer and floating point fields moshi defaults to Float/Double for numeric fields when parsing to Any.
And the issue here is in the API, it should not return different type values for same query. at the very least there should be an indication as to the type of data. What happens if you receive a string value which actually looks like a number?

How do I need to write the 3rd line - Not enough information to infer type variable T

I'm new to Kotlin and try to convert a project from Java to Kotlin
I just need one last step and I don't understand what's going on :(
I'm getting a Not enough information to infer type variable T on model.predict call
override fun link(word: String): LinkSuggestion {
val input: DoubleArray = gramToInt.toArray(word)
val output: Array<Any> = model.predict(input)
// ~~~~~~~ Not enough information to infer type variable T
val maxPredictionIndex: Int = (output[output.size - 1] as Long).toInt()
val maxPredictionProbability: Double = output[maxPredictionIndex] as Double
return LinkSuggestion(word, intToLink.fromInt(maxPredictionIndex), maxPredictionProbability)
}
where model is import org.pmml4s.model.Model
The previous Java code:
#Override
public LinkSuggestion link(String word) {
double[] input = gramToInt.toArray(word);
Object[] output = model.predict(input);
int maxPredictionIndex = ((Long) output[output.length - 1]).intValue();
double maxPredictionProbability = (Double) output[maxPredictionIndex];
return new LinkSuggestion(word, intToLink.fromInt(maxPredictionIndex), maxPredictionProbability);
}
I needed to write
val output: Array<Any> = model.predict<Any>(input)

Kotlin/Native: How to convert cArrayPointer to Array

How can I convert cArrayPointer to a simple Array/List when using c-interop?
val myArray: Array<Int> = memScoped {
val cArray = allocArray<IntVar>(5)
fill(cArray)
cArray.toSimpleArray() <--- There is no such function
}
I'd recommend to make it somehow like this:
val myArray: Array<Int> = memScoped {
val length = 5 //cause I don't know how to get C-array size
val cArray = allocArray<IntVar>(length)
(0 until length).map { cArray[it] }.toTypedArray()
}
As one can see in the documentation, CArrayPointer is nothing but a typealias of CPointer. So, I suppose there can't be anadditional functionality, like one you desire.

Creating a map with values containing generics

I want to create a map with a key being a single object and a value being many objects, some containing generics. Is there a concise way to do this in Kotlin? I've used data classes in the past, but haven't found a way to make that work with generics.
Thanks!
Edit: Here's an example:
class SomeClass<E> {
data class Data(val str: String, val int: Int, val e: E) //the last value is invalid
val map: MutableMap<String, Data> = mutableMapOf()
}
Working from your example, this should work for you.
data class Data<E>(val str: String, val int: Int, val e: E)
class SomeClass<E> {
val map: MutableMap<String, Data<E>> = mutableMapOf()
}
I'm defining Data as an external, generic class, and use that inside the actual class.
Edit: Actually, you don't even need to move the data class outside of the outer class:
class SomeClass<E> {
data class Data<T>(val str: String, val int: Int, val e: T)
val map: MutableMap<String, Data<E>> = mutableMapOf()
}

Kotlin JS - string to number conversion?

How to do String to number conversion in Kotlin JS app. I am Using the following code and having some trouble converting from the HTMLInputElement value to double.
fun c2f(self: Any) {
console.log("Self object: ${self} ")
val celsius = document.getElementById("celcius")?.getAttribute("value") as Double
val fahrenheit = celsius * 1.8 + 32
console.log("Fahrenheit value: ${fahrenheit} ")
window.alert("Celcius (${celsius}) -> Fahrenheit (${fahrenheit}) ")
}
Also i am not seeing any toDouble() function on String class as in the case of JVM app.
Answering my own question as this would be helpful for somebody.
You can use the kotlin.js top level parse functions for string <-> Number conversion.
fun parseInt(s: String, radix: Int = 10): Int
fun safeParseInt(s : String) : Int?
fun safeParseDouble(s : String) : Double?
Simply use the String.toXXX() functions, e.g.
val n = "1"
val m = 2 + n.toInt()
val x = "1.1"
val y = 2.0 + x.toFloat()
etc.