How to initialize nested data class in Kotlin? - function

I have the following structure in a data class:
data class A(
val b: Int,
val c: C
) {
data class B(
val d: Int
)
data class C(
val d: Int
)
}
and an instance of this class is being passed to a method which has the following signarure:
fun doSomethingMethod(object: A.B?): Mono<Unit> =
// do something
}
So now I am trying to initialize an instance of the data class A with only initializing B as wel but I dont understand how to do it. So far I have tried:
val testObject = A(A.B(5))
But its not working. Anyone has an idea?

To create an object of nested data class just use next syntax:
val instance = OuterClass.NestedClass([params])
In your case it will be:
val b = A.B(5)
Complete example:
fun doSomethingMethod(b: A.B?): Mono<Unit> {
// do something
}
val b = A.B(5)
val mono = doSomethingMethod(b)

Related

How to parse generic key with kotlin serialization from JSON

I am struggling with come up with idea how to properly parse JSON like this:
{
"generic_key": { "version":1, "ttl":42 }
}
where expected kotlin class should look like this:
#Serializable
data class Config(val version: Int, val ttl: Long) {
#Transient
var key: String? = null // <== here comes generic_key
}
UPDATE
What I want to achieve is to get a kotlin class from string JSON and I don't know what key will be used as "generic_key".
UPDATE 2
Even something like this is okey for me:
#Serializable
data class ConfigWrapper(val map: Map<String, Config>)
Where there would be map with single item with key from jsonObject (e.g. generic_key) and with rest parsed with standard/generated Config.serializer.
Option 1. Define a custom deserializer, which will use plugin-generated serializer for Config class:
object ConfigDeserializer : DeserializationStrategy<Config> {
private val delegateSerializer = MapSerializer(String.serializer(), Config.serializer())
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return v.apply { key = k }
}
}
To use it, you'll need to manually pass it to the decodeFromString method:
val result: Config = Json.decodeFromString(ConfigDeserializer, jsonString)
Option 2. Define a surrogate for Config class and a custom serializer, which will use plugin-generated serializer for ConfigSurrogate class, so that you could reject plugin-generated serializer for Config class and wire this custom serializer to Config class:
#Serializable
#SerialName("Config")
data class ConfigSurrogate(val version: Int, val ttl: Long)
object ConfigSerializer : KSerializer<Config> {
private val surrogateSerializer = ConfigSurrogate.serializer()
private val delegateSerializer = MapSerializer(String.serializer(), surrogateSerializer)
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return Config(v.version, v.ttl).apply { key = k }
}
override fun serialize(encoder: Encoder, value: Config) {
surrogateSerializer.serialize(encoder, ConfigSurrogate(value.version, value.ttl))
}
}
#Serializable(with = ConfigSerializer::class)
data class Config(val version: Int, val ttl: Long) {
// actually, now there is no need for #Transient annotation
var key: String? = null // <== here comes generic_key
}
Now, custom serializer will be used by default:
val result: Config = Json.decodeFromString(jsonString)
Use the following data classes
data class Config(
#SerializedName("generic_key" ) var genericKey : GenericKey? = GenericKey()
)
data class GenericKey (
#SerializedName("version" ) var version : Int? = null,
#SerializedName("ttl" ) var ttl : Int? = null
)
If the key is dynamic and different, the map structure should be fine
#Serializable
data class Config(val version: Int, val ttl: Long)
val result = JsonObject(mapOf("generic_key" to Config(1, 42)))
At the end this works for me, but if there is more straight forward solution let me know.
private val jsonDecoder = Json { ignoreUnknownKeys = true }
private val jsonConfig = "...."
val result = jsonDecoder.parseToJsonElement(jsonConfig)
result.jsonObject.firstNonNullOf { (key, value) ->
config = jsonDecoder.decodeFromJsonElement<Config>(value).also {
it.key = key // this is generic_key (whatever string)
}
}

convert json string to case class object from given json string and type of case class

Requirement is to convert json string to case class object in scala given jsonString and the type of the case class.
I have tried Gson and jackson libraries, but not able to solve the given requirment.
package eg.json
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.gson.Gson
import com.typesafe.scalalogging.LazyLogging
case class Person(name : String, age : Int)
case class Address(street : String, buildingNumber : Int, zipCode : Int)
case class Rent(amount : Double, month : String)
//there are many other case classes
object JsonToObject extends LazyLogging{
import logger._
def toJsonString(ref : Any) : String = {
val gson = new Gson()
val jsonString = gson.toJson(ref)
jsonString
}
def main(args: Array[String]): Unit = {
val person = Person("John", 35)
val jsonString = toJsonString(person)
//here requirement is to convert json string to case class instance, provided the type of case class instance
val gsonObj = toInstanceUsingGson( jsonString, Person.getClass )
debug(s"main : object deserialized using gson : $gsonObj")
val jacksonObj = toInstanceUsingJackson( jsonString, Person.getClass )
debug(s"main : object deserialized using gson : $jacksonObj")
}
def toInstanceUsingGson[T](jsonString : String, caseClassType : Class[T]) : T = {
val gson = new Gson()
val ref = gson.fromJson(jsonString, caseClassType)
ref
}
def toInstanceUsingJackson[T](jsonString : String, caseClassType : Class[T]) : T = {
val mapper = new ObjectMapper()
val ref = mapper.readValue(jsonString, caseClassType)
ref
}
}
Output of execution of above code is :-
01:32:52.369 [main] DEBUG eg.json.JsonToObject$ - main : object deserialized using gson : Person
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "name" (class eg.json.Person$), not marked as ignorable (0 known properties: ])
at [Source: (String)"{"name":"John","age":35}"; line: 1, column: 10] (through reference chain: eg.json.Person$["name"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:60)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:822)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1152)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1567)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at eg.json.JsonToObject$.toInstanceUsingJackson(JsonToObject.scala:49)
at eg.json.JsonToObject$.main(JsonToObject.scala:34)
at eg.json.JsonToObject.main(JsonToObject.scala)
Kindly suggest, how to achieve this using gson or jackson, or suggest some other library with sample example.
Above simplified problem is on github :-
https://github.com/moglideveloper/JsonToScalaObject
With Jackson you can do it like this:
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
val mapper = new ObjectMapper() with ScalaObjectMapper
//this line my be needed depending on your case classes
mapper.registerModule(DefaultScalaModule)
def fromJson[T](json: String)(implicit m: Manifest[T]): T = {
mapper.readValue[T](json)
}
I think it is really clean with Jackson lib.
The usage is like this:
val json: String = ???
val personObject: Person = fromJson[Person](json)
Try using circe by Cats.
add circe to your project (https://circe.github.io/circe/ - Quick Start).
create a case class that represent what you want to build from your json.
declare a decoder
https://circe.github.io/circe/codecs/semiauto-derivation.html
https://github.com/circe/circe
import io.circe.parser.decode
import io.circe.syntax._
case class DataToDecode(name : String,
age : Int,
street : String,
buildingNumber : Int,
zipCode : Int,
amount : Double,
month : String)
object DataToDecode{
implicit val dataToDecode: Decoder[DataToDecode] = deriveDecoder
def decodeData(data: Json) : DataToDecode {
data.as[DataToDecode].right.get
}
}
nice example here

how to pack zeros in to bundles in chisel

I have a question on pack zeros into bundles. For example consider the next code:
class CmplxNum(val bitwidth: Int) extends Bundle {
val real = SInt(INPUT,bitwidth.W)
val imag = SInt(INPUT,bitwidth.W)
}
class MyClass extends Module {
val io = IO(new Bundle {
val in = new CmplxNum(16)
val load = Bool(INPUT)
val clr = Bool(INPUT)
...
})
...
val sample = RegEnable(io.in,0.S,io.load) // <-- how do i set the reset value
When(io.clr) {
sample <> sample.fromBits(0.S) // <-- I tried this it compiles, but dont know if it is correct
}
}
How do I pack zeros into this Bundle in the RegEnable & clr cases ?
For RegEnable I've got elaboration error of type miss-match which make sense
Here is one way. It relies on the relatively new BundleLiterals (new CmplxNum(16)).Lit(_.real -> 0.S, _.imag -> 0.S). I have also refactored your code a little bit to use the current chisel3 idioms. Without a specific need I would not recommend placing your Input/Output in Bundle. Also the more modern way is to wrap the IO fields in Input() or Output()
import chisel3._
import chisel3.util.RegEnable
import chisel3.experimental.BundleLiterals._
class CmplxNum(val bitwidth: Int) extends Bundle {
val real = SInt(bitwidth.W)
val imag = SInt(bitwidth.W)
}
class MyClass extends Module {
val io = IO(new Bundle {
val in = Input(new CmplxNum(16))
val load = Input(Bool())
val clr = Input(Bool())
...
})
...
val sample = RegEnable(
io.in,
init = (new CmplxNum(16)).Lit(_.real -> 0.S, _.imag -> 0.S),
enable = io.load
)
when(io.clr) {
sample <> sample.fromBits(0.S) // <-- I tried this it compiles, but dont know if it is correct
}
}

Parse a nested JSON with Kotlinx.Serialization

I've been playing with Kotlinx.serialization, and I have been trying to parse a substring:
Given a JSON like:
{
"Parent" : {
"SpaceShip":"Tardis",
"Mark":40
}
}
And my code is something like:
data class SomeClass(
#SerialName("SpaceShip") ship:String,
#SerialName("Mark") mark:Int)
Obviously, Json.nonstrict.parse(SomeClass.serializer(), rawString) will fail because the pair "SpaceShip" and "Mark" are not in the root of the JSON.
How do I make the serializer refer to a subtree of the JSON?
P.S: Would you recommend retrofit instead (because it's older, and maybe more mature)?
#Serializable
data class Parent(
#SerialName("Parent")
val someClass: SomeClass
)
#Serializable
data class SomeClass(
#SerialName("SpaceShip")
val ship: String,
#SerialName("Mark")
val mark: Int
)
fun getSomeClass(inputStream: InputStream): SomeClass {
val json = Json(JsonConfiguration.Stable)
val jsonString = Scanner(inputStream).useDelimiter("\\A").next()
val parent = json.parse(Parent.serializer(), jsonString)
return parent.someClass
}
import kotlinx.serialization.*
import kotlinx.serialization.json.Json
#Serializable
data class Parent(
#SerialName("Parent")
val parent: SomeClass
)
#Serializable
data class SomeClass(
#SerialName("SpaceShip")
val ship:String,
#SerialName("Mark")
val mark:Int
)
fun main() {
val parent = Json.parse(Parent.serializer(), "{\"Parent\":{\"SpaceShip\":\"Tardis\",\"Mark\":40}}")
println(parent)
}

scala, using class member function as first class functions

I want to assign a member function of a class instance as a first class function to a variable:
class A(val id:Int){ def f(u:Int)=id+u }
val a= new A(0)
val h=a.f // fails: interpreted as a.f(with missing parameter u)
val h1 = (u:Int)=>a.f(u) // OK and does what we want
We can get the desired effect by assigning an appropriate anonymous function.
Is this the only way?
I searched but could find no reference at all.
Use a placeholder to indicate it is partially applied:
scala> class A(val id:Int){ def f(u:Int)=id+u }
defined class A
scala> val a = new A(0)
a: A = A#46a7a4cc
scala> val h = a.f _
h: Int => Int = <function1>
scala> h(2)
res0: Int = 2
EDIT
Trying the code out in the REPL prints
scala> val h = a.f
<console>:9: error: missing arguments for method f in class A;
follow this method with `_' if you want to treat it as a partially applied function
val h = a.f
^