How to convert JAVA POJO class to Scala Case Class with SnakeCase - scala

In Scala, I am trying to convert from Java POJO class to Scala Case class. Please below find the details:
//Java POJO Response Class
#JsonIgnoreProperties(ignoreUnknown = true)
public class IncidentResponse {
private String issueType;
private List<OrderDetailResponse> orderDetails;
//GETTER and SETTERs}
#JsonIgnoreProperties(ignoreUnknown = true)
public class OrderDetailResponse {
private String orderId;
private String orderItemId;
//GETTER and SETTERs
public List<OrderDetailResponse> getOrderDetails() {return this.orderDetails;}}
//Scala Case Class
#JsonNaming(classOf[SnakeCaseStrategy])
#JsonIgnoreProperties(ignoreUnknown = true)
case class IncidentDetails(issueType: String,
orderDetails: List[OrderDetailResponse])
//OrderDetailResponse is from the Scala Case Class
#JsonNaming(classOf[SnakeCaseStrategy])
#JsonIgnoreProperties(ignoreUnknown = true)
case class OrderDetailResponse(orderId:String,
orderItemId:String)
I have tried the below ways to convert the data in SnakeCase -
Variable "data" contains the IncidentResponse of JAVA POJO Class
Using asInstanceof List[Map] and then using Object Mapper convert it to the OrderDetailResponse
val orderDetails = data.getOrderDetails.asInstanceOf[List[Map[String, Any]]].map(p => (mapper.convertValue(p, typeReference[OrderDetailResponse](tTag)).asInstanceOf[OrderDetailResponse].get))
IncidentDetails(data.getIssueType,orderDetails)
Got the error as - java.util.ArrayList cannot be cast to scala.collection.immutable.List
Tried to convert List[Map[Object,Object]]:
val scalaList =data.getOrderDetails.asScala.toList.asInstanceOf[List[Map[Object,Object]]]
IncidentDetails(data.getIssueType,
scalaList.asInstanceOf[List[OrderDetailResponse]])
Didn't get any error with this, however it was not converted to SnakeCase of OrderDetailResponse but issueType was converted to SnakeCase
If i assign values individually from the POJO class then it will work, if I have many fields then it will leads to many lines of code so how to convert to SnakeCase of Scala case class from Java POJO class.
IncidentDetails(data.getIssueType,
data.getOrderDetails.asScala.head.map(orderResp =>
OrderDetailResponse(orderResp.getOrderId,orderResp.getOrderItemId)
)

Related

How to implement enums with values in Scala 2.12.15

In Java, one can use (from https://www.baeldung.com/java-enum-values#adding-constructor)
public enum Element {
H("Hydrogen"),
HE("Helium"),
NE("Neon");
public final String label;
private Element(String label) {
this.label = label;
}
}
to construct Enums with string values, such that NE.label would yield "Neon".
How do you do the same thing, but in Scala version 2.12.15? (Please show the above example translated to Scala)
The best way to create enums in Scala is through a basic ADT like this:
sealed abstract class Element(val label: String)
object Element {
final case object H extends Element(label = "Hydrogen")
final case object HE extends Element(label = "Helium")
final case object NE extends Element(label = "Neon")
}
If you want some goodies, like getting the list of all elements or a getByName method, for free.
Then the best would be to combine the above with the Enumeratum library.

Why is Lombok in a Scala case class inaccessible in a Java class when using Mixed project?

I have a simple Spring boot app with a Scala class...
case class TestThing(val name: String ){
#Getter
#Setter
var value = null
def getMap = {
val list: List[Item] = List(Item("1", "Foo"), Item("2", "Bar"))
val map = list.map(item => item.key -> item).toMap
map("1")
}
}
Now I am trying to access the getter and setter function from a java class like this...
#GetMapping("/other")
public String index(){
TestThing thing = new TestThing("My Name");
thing.setValue("Test");
return "Hello World from me "+thing.getMap().value()+"||"+thing.getValue();
}
The thing.getMap() works fine but I get the following compile error for the getters and setters...
error: cannot find symbol
return "Hello World from me "+thing.getMap().value()+"||"+thing.getValue();
^
symbol: method getValue()
location: variable thing of type TestThing
What am I missing? I found this question (Error compiling Java/Scala mixed project and Lombok) but it is the reverse and doesn't seem to help.
Lombok doesn't work with Scala. Simple as that. (It is even described why in the issue you linked). The #Getter and #Setter annotations in Scala classes are never processed and the accessors are never generated.
It is also completely unneeded as case classes generate: toString, equals, hashcode getters and setter. If you want to have Java Bean accessors you can use #BeanProperty annotation.
import scala.beans.BeanProperty
case class TestThing(val name: String ){
#BeanProperty
var value: String = null
}
val test = TestThing("test")
test.getValue // null
test.setValue("test")
test.getValue // "test"

Using Java Domain Object Instead of Scala Case Class in Spark DataSet Creation

I'm trying to create a Spark DataSet from an RDD using the RDD#toDS method.
However, instead of using the Scala case class to specify the schema, I want to use an existing domain object defined in a 3rd party library. But, when I do that I get the following error:
scala> import org.hl7.fhir.dstu3.model.Patient
import org.hl7.fhir.dstu3.model.Patient
scala> val patients = sc.loadFromMongoDB(ReadConfig(Map("uri" -> "mongodb://mongodb/fhir.patients")))
patients: com.mongodb.spark.rdd.MongoRDD[org.bson.Document] = MongoRDD[0] at RDD at MongoRDD.scala:47
scala> val patientsDataSet = patients.toDS[Patient]()
<console>:44: error: not enough arguments for method toDS: (beanClass: Class[org.hl7.fhir.dstu3.model.Patient])org.apache.spark.sql.Dataset[org.hl7.fhir.dstu3.model.Patient].
Unspecified value parameter beanClass.
val patientsDataSet = patients.toDS[Patient]()
^
This is what I get when I remove the parenthesis:
scala> val patientsDataSet = patients.toDS[Patient]
<console>:46: error: missing arguments for method toDS in class MongoRDD;
follow this method with `_' if you want to treat it as a partially applied function
val patientsDataSet = patients.toDS[Patient]
Is there anyway, I can use a Java Object in place of a case class here?
Thanks!
It might work to create a case class that extends your java object.
Java:
public class Patient {
private final String name;
private final String status;
public Patient(String name, String status) {
this.name = name;
this.status = status;
}
public String getName() {
return name;
}
public String getStatus() {
return status;
}
}
Scala:
case class Patient0(name: String, status: String) extends Patient(name, status)
val patientsDataSet = patients.toDS[Patient]()
val patients = sc.loadFromMongoDB(ReadConfig(Map("uri" -> "mongodb://mongodb/fhir.patients")))

Scala: Get value of child from parent?

Trying to get values of all fields of child from parent class like this:
for (field <- this.getClass.getDeclaredFields) {
Logger.debug(field.getName)
field.get(this)
}
and got error
Exception: Class models.Model$$anonfun$4 can not access a member of
class models.Good with modifiers "private"
at line
field.get(this)
In Good class I don't have private fields:
class Good(id: Option[String]) extends Model[realGood](id){
lazy val title: String = this.load[String](realObject.get.title)
lazy val cost: Double = this.load[Double](realObject.get.cost)
}
What's wrong with this code?
As hinted in the comments, Scala's conversion to java bytecode isn't always straightforward (though it's usually pretty predictable, once you get the hang of it). In particular, public fields in Scala compile to a private field with a public getter in java bytecode:
fukaeri:~ dlwh$ cat zzz.scala
class Good(id: Option[String]) {
lazy val title: String = ???
lazy val cost: Double = ???
}
fukaeri:~ dlwh$ scalac zzz.scala
fukaeri:~ dlwh$ javap -private Good
Compiled from "zzz.scala"
public class Good {
private java.lang.String title;
private double cost;
private volatile byte bitmap$0;
private java.lang.String title$lzycompute();
private double cost$lzycompute();
public java.lang.String title();
public double cost();
public Good(scala.Option<java.lang.String>);
}
You can see that Good has private fields for each of your declared public fields, in addition to public getters. Because the fields are lazy val, they also have computation methods for initialization, and there's a bitmap$0 field to ensure that the lazy vals are initialized only once.
In your loop, you can use field.setAccessible(true) to fix your exception.

How to implement interface Serializable in scala?

I have scala class like:
#Entity("users")
class User(#Required val cid: String, val isAdmin: Boolean = false, #Required val dateJoined: Date = new Date() ) {
#Id var id: ObjectId = _
#Reference
val foos = new ArrayList[Foo]
}
If it was a Java class I would simply put implements java.io.Serializable but this does not work in scala. Also is foos as declared above is private or public?
How do I use a #serializable scala object?
foos is public unless marked otherwise
scala 2.9.x also have an interface named Serializable, you may extends or mixin this. before 2.9.x the #serializable is the only choice.
You can add Serialized annotation on your Scala Class (at JPA Entity for example):
Because Serializable is a trait, you can mix it into a class, even if
your class already extends another class:
#SerialVersionUID(114L)
class Employee extends Person with Serializable ...
Se more details at this link:
https://www.safaribooksonline.com/library/view/scala-cookbook/9781449340292/ch12s08.html
An example of my Entity (JPA) class writed in scala, using Serialized properties:
import javax.persistence._
import scala.beans.BeanProperty
import java.util.Date
#SerialVersionUID(1234110L)
#Entity
#Table(name = "sport_token")
class Token() extends Serializable {
#Id
#SequenceGenerator(name="SPORT_TOKEN_SEQ",catalog="ESPORTES" , sequenceName="SPORT_TOKEN_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE , generator="SPORT_TOKEN_SEQ")
#BeanProperty
var id: Int = _
#BeanProperty
#Column(name="token")
var token: String = _
#BeanProperty
#Column(name="active")
var active: Int = _
}