Add null to int column - scala

I have a scala code as below
case class Employee(firstName: String, lastName: String, email: String, salary: Int)
val employee = new Employee("John", null, "john-doe#some.edu", null)
It fails with the below error
error: an expression of type Null is ineligible for implicit conversion
How do i add Null to int salary column?

Int is a primitive type and extends AnyVal which cannot hold null.
null can only be used by AnyRef types.
For Int, null translates to 0.
Refer:
scala> null.asInstanceOf[Int]
res0: Int = 0
scala> null.asInstanceOf[String]
res1: String = null
You could instantiate your class as below:
scala> val employee = new Employee("John", null, "john-doe#some.edu", null.asInstanceOf[Int])
employee: Employee = Employee(John,null,john-doe#some.edu,0)

Related

Scala Play Framework: cannot generate object from json with null values

I'm new to Scala and the Play Framework. I have written the following controller:
#Singleton
class MyController #Inject()(val controllerComponents: ControllerComponents) extends BaseController {
implicit val newMeasurementJson: OFormat[MeasurementModel] = Json.format[MeasurementModel]
def addMeasurement(): Action[AnyContent] = Action { implicit request =>
val content = request.body
val jsonObject: Option[JsValue] = content.asJson
val measurement: Option[MeasurementModel] =
jsonObject.flatMap(
Json.fromJson[MeasurementModel](_).asOpt
)
...
}
...
}
Where the endpoint receives the following JSON:
{
"sensor_id": "1029",
"sensor_type": "BME280",
"location": 503,
"lat": 48.12,
"lon": 11.488,
"timestamp": "2022-04-05T00:34:24",
"pressure": 94667.38,
"altitude": null,
"pressure_sealevel": null,
"temperature": 3.91,
"humidity": 65.85
}
MeasurementModel looks like this:
case class MeasurementModel(
sensor_id: String,
sensor_type: String,
location: Int,
lat: Float,
lon: Float,
timestamp: String,
pressure: Float,
altitude: Int,
pressure_sealevel: Int,
temperature: Float,
humidity: Float) {
}
Through testing I have seen that the null values in the JSON are causing the creation of the measurement object to be unsuccessful. How can I successfully handle null values and have them set in the generated MeasurementModel object?
The datatypes that can store null are Null and Option[].
Consider the following REPL code:
`
scala> val mightBeIntOrNull: Option[Int] = Option(1)
val a: Option[Int] = Some(1)
scala> val mightBeIntOrNull: Option[Int] = null
val a: Option[Int] = null
The Option wraps the Int value in Some, which can be extracted by pattern matching.
scala> val mightBeIntOrNull: Option[Int] = Option(1)
val mightBeIntOrNull: Option[Int] = Some(1)
scala> mightBeIntOrNull match {
| case Some(myIntVal) => println("This is an integer :" + myIntVal)
| case _ => println("This might be a null")
| }
This is an integer :1
scala> val mightBeIntOrNull: Option[Int] = null
val mightBeIntOrNull: Option[Int] = null
scala> mightBeIntOrNull match {
| case Some(myIntVal) => println("This is an integer :" + myIntVal)
| case _ => println("This might be a null")
| }
This might be a null
As Gaƫl J mentioned, you should add Option for the desired datatype in your case class
So the solution can be to wrap the datatype in option where you expect a null. Like:
{
"altitude": Option[Float],
"sensor_type": Option[String],
}

Scala Slick store only selected fields of the case class

I have 2 case classes
case class PPU(id : String,
name : String,
mini_ppus : List[MiniPPU]..)
case class MiniPPU( minPpuId : String,
ppu_id : String.. )
I have a PPU table and want to store only id and name but not the mini_ppus.
class PpuTable(tag: Tag) extends Table[PPU](tag, None, _tableName ="ppu") {
def * = (id, name) <> (
{ tuple: (String,String) =>
PPU(id.asInstanceOf[String], name.asInstanceOf[String], ListBuffer[MiniPPU]().toList)},
{ ppuTable: PPU => Option(ppuTable.id, ppuTable.name) }
)
val id = column[String]("ppu_id", O.PrimaryKey)
val name: Rep[String] = column[String]("name")
}
Whenever im trying to select something from the table, i'm getting an error:
java.lang.ClassCastException: slick.relational.RelationalTableComponent$Table$$anon$1 cannot be cast to java.lang.String
what is the correct way to override def * method ?
Instead of this:
{ tuple: (String,String) => PPU(id.asInstanceOf[String], name.asInstanceOf[String], ListBuffer[MiniPPU]().toList)}
try this:
{ tuple: (String, String) => PPU(tuple._1, tuple._2, ListBuffer[MiniPPU]().toList)}
Use the values from the tuple, not the ones declared before <>

Scala Get All Fields without default value

I have the following code:
case class Person(name: String, age: Int = 0)
def extractFieldNames[A](implicit m: Manifest[A]): Array[String] =
m.runtimeClass.getDeclaredFields.map(_.getName)
extractFieldNames[Person]
// should return Array("name", "age")
But what if I want to exclude age because of its default parameter? How would I do this?

How To Access access Case class field Value from String name of the field

How should I extract the value of a field of a case class from a given String value representing the field.
For example:
case class Person(name: String, age: Int)
val a = Person("test",10)
Now here given a string name or age i want to extract the value from variable a. How do i do this? I know this can be done using reflection but I am not exactly sure how?
What you're looking for can be achieve using Shapeless lenses. This will also put the constraint that a field actually exists on a case class at compile time rather than run time:
import shapeless._
case class Person(name: String, age: Int)
val nameLens = lens[Person] >> 'name
val p = Person("myName", 25)
nameLens.get(p)
Yields:
res0: String = myName
If you try to extract a non existing field, you get a compile time error, which is a much stronger guarantee:
import shapeless._
case class Person(name: String, age: Int)
val nonExistingLens = lens[Person] >> 'bla
val p = Person("myName", 25)
nonExistingLens.get(p)
Compiler yells:
Error:(5, 44) could not find implicit value for parameter mkLens: shapeless.MkFieldLens[Person,Symbol with shapeless.tag.Tagged[String("bla")]]
val nonExistingLens = lens[Person] >> 'bla
don't know exactly what you had in mind, but a match statement would do, it is not very generic or extensible with regards changes to the Person case class, but it does meet your basic requirements of not using reflection:
scala> val a = Person("test",10)
a: Person = Person(test,10)
scala> def extract(p: Person, fieldName: String) = {
| fieldName match {
| case "name" => p.name
| case "age" => p.age
| }
| }
extract: (p: Person, fieldName: String)Any
scala> extract(a, "name")
res1: Any = test
scala> extract(a, "age")
res2: Any = 10
scala> extract(a, "name####")
scala.MatchError: name#### (of class java.lang.String)
at .extract(<console>:14)
... 32 elided
UPDATE as per comment:
scala> case class Person(name: String, age: Int)
defined class Person
scala> val a = Person("test",10)
a: Person = Person(test,10)
scala> def extract(p: Person, fieldName: String) = {
| fieldName match {
| case "name" => Some(p.name)
| case "age" => Some(p.age)
| case _ => None
| }
| }
extract: (p: Person, fieldName: String)Option[Any]
scala> extract(a, "name")
res4: Option[Any] = Some(test)
scala> extract(a, "age")
res5: Option[Any] = Some(10)
scala> extract(a, "name####")
res6: Option[Any] = None
scala>
I think it can do by convert case class to Map, then get field by name
def ccToMap(cc: AnyRef) =
(Map[String, Any]() /: cc.getClass.getDeclaredFields) {
(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
Usage
case class Person(name: String, age: Int)
val column = Person("me", 16)
println(ccToMap(column))
val name = ccToMap(column)["name"]

Non-value field is accessed in 'hashCode()'

It happen with id. (Non-value field is accessed in 'hashCode()')
How can I fix it?
Here is my code:
import java.util.Date
case class Category() {
var id: Integer = _
var parentId: Integer = _
var name: String = _
var status: Boolean = _
var sortOrder: Integer = _
var createTime: Date = _
var updateTime: Date = _
def this(id: Integer, parentId: Integer, name: String, status: Boolean, sortOrder: Integer, createTime: Date, updateTime: Date) {
this()
this.id = id
this.parentId = parentId
this.name = name
this.status = status
this.sortOrder = sortOrder
this.createTime = createTime
this.updateTime = updateTime
}
override def hashCode(): Int = {
if (id != null) id.hashCode() else 0
}
}
If I change var id to val id, it will be ok. But I need setter, I cant't set it as a val.
Non value field in hashCode means you're relaying on a mutable field to generate the classes hashcode, which may be dangerous, depending how you use that class. For example, assume you have a HashMap[Category, String], you place an entry in the hash map and them mutate the id field, now that hashmap can no longer find the class instance by it's hashcode.
You're treating a case class like it's a regular, mutable Java class. Case classes provides the mechanism of encapsulating immutable data in a syntax convenient way (where you also get a few nice things for free, such as hashCode, equals, etc). What you can do is make your case class accept parameters via it's primary constructor:
case class Category(id: Int,
parentId: Int,
name: String,
status: Boolean,
sortOrder: Int,
createTime: Date,
updateTime: Date) {
override def hashCode(): Int = id.hashCode()
}
If you want to update Category, you can use the utility copy method defined on a case class:
val cat = Category(id = 1,
parentId = 1,
name = "Hello",
status = true,
sortOrder = 1,
createTime = new Date(123132123L),
updateTime = new Date(52234234L))
val newCat: Category = cat.copy(id = 2)
Another way of approaching this (which I like less TBH) would be to make all your fields an Option[T] with a default value set to None:
case class Category(id: Option[Int] = None,
parentId: Option[Int] = None,
name: Option[String] = None,
status: Option[Boolean] = None,
sortOrder: Option[Int] = None,
createTime: Option[Date] = None,
updateTime: Option[Date] = None) {
override def hashCode(): Int = id.map(_.hashCode()).getOrElse(0)
}
And then updating them once you can populate the class with data:
val cat = Category()
val newCat = cat.copy(id = Some(1))
After I try many times, I think this should be a good choice.
Any ideas?
override def hashCode(): Int = {
if (getId != null) getId.hashCode() else 0
}