xml data looks like
<bd>
<oied>
<oeuo>XYZWC999</oeuo>
<oedo>SorNbteluk=ONRM_ROOT_MO_R,SrobeNhbwk=XYZWC999,MoetxeoCt=XYZWC999</oedo>
<oesw>CXP9021776/2_R2CA15</oesw>
</oied>
<bi>
<bts>20150205141500Z</bts>
<gp>900</gp>
<bt>paaoCukStSteboshRrttcps</bt>
<bt>pptubthCaStctoSekSos</bt>
<bv>
<biod>MaebdlgeaooeEt=1,TparswotterNorok=1,Ntcp=Kub-9</biod>
<r>4578</r>
<r>10769</r>
</bv>
<bv>
<biod>MEegoedbaaloet=1,TreatoorNtosrpwk=1,Ntcp=1</biod>
<r>11021</r>
<r>86235</r>
</bv>
<bv>
<biod>MdaolaeeobeEgt=1,TretrowooNrtsapk=1,Nctp=Kub-7</biod>
<r>0</r>
<r>0</r>
</bv>
</bi>
</bd>
I am new to Scala, I could figure out the basic structure.
case class xmldata(oeuo : String, oedo : String, oesw: String, bts: String, gp : Int, btArray : List[String])
What is the optimized Scala case class (with collections) for this xml data?
It's just tree of case classes:
case class Bd(oied: Oied, bi: Bi)
case class Oied(oeuo: String, oedo: String, oesw: String)
case class Bi(bts: String, gp: String, bt: List[String], bv: List[Bv])
case class Bv(biod: String, r: List[String])
If order doesn't matter - you might use Set instead of List
You also might flatten it a bit (like in your solution), but it could be harder to map it with xml-binding tools then
case class Bd(oeuo: String, oedo: String, oesw: String, bts: String, gp: String, bt: List[String], bv: List[Bv])
The most flattened and least operable version (not recommended):
case class Bd(oeuo: String, oedo: String, oesw: String, bts: String, gp: String, bt: List[String], biods: List[String], rs: List[List[String]])
Related
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
teamId: String,
actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
With the trait:
trait Event {
var teamId: String
var actorId: String
}
Produces this error:
class CaseClassJobEvent needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class CaseClassJobEvent, the missing signatures are as follows.
[error] * For convenience, these are usable as stub implementations.
[error] */
[error] def actorId_=(x$1: String): Unit = ???
[error] def teamId_=(x$1: String): Unit = ???
[error] case class CaseClassJobEvent(
[error] ^
What am I doing wrong? What should I be doing? Is there anyway to use traits or inheritance to enforce properties on a case class? I don't want to enforce methods just properties.
Luis Miguel Mejía Suárez already answered your question, so I thought I would add something here so you might reconsider the design of your class.
Instead of creating a var in your case class, use the .copy method for case class to generate a new object with the changed fields.
So, if you want to change teamId or actorId in your case class do something like this:
val jobEvent = CaseClassJobEvent(...)
val changedJobEvent = jobEvent.copy( teamId = "somenewvalue", actorId = "somenewvalue" )
To answer your original question:
trait Event {
def teamId: String
def actorId: String
}
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
var teamId: String,
var actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
A design like this is probably what you wanted. It's fine for a school project, but you would never do something like this in production, especially if you are working in a multithreaded environment. Immutable values are always thread-safe, and you would be breaking that by adding vars to your class.
Just something to consider.
=======
We are currently discussing how to implement CaseClassJobEvent without using mutable values. Here is my suggested implementation.
trait Event {
def teamId: String
def actorId: String
}
case class CaseClassJobEvent(
jobId: String,
jobType: Option[String],
inPlanning: Option[Boolean],
teamId: String,
actorId: String,
adminActorId: Option[String],
sessionId: String,
clientSessionId: Option[String],
clientCreatedAt: Long,
seqId: Long,
isSideEffect: Option[Boolean],
opAction: String,
stepId: Option[String],
jobBaseStepId: Option[String],
fieldId: Option[String],
serverReceivedAt: Option[Long]) extends Event
Everything is the same as the solution you want except teamId and actorId are not vars.
If you need to change the value of teamId and actorId in your case class to something else, do something like this:
def setTeamIdAndActorId(myEvent: CaseClasJobEvent, newTeamId: Option[String], newActorId: Option[String]): CaseClassJobEvent = {
val newEvent1 = if (newTeamId.isDefined) myEvent.copy(teamId = newTeamId.get) else myEvent
val newEvent2 = if (newactorId.isDefined) newEvent1.copy(actorId = newActorId.get) else newEvent1
newEvent2
}
If this seems like a horribly verbose way to have to modify a case class, you're right. What we currently do at our company is use the quicklens library from softwaremill to modify deeply nested case classes with more elegant syntax. It is still not as simple as reassigning a var, but it is more correct in a multithreaded environment, and it is less verbose than calling copy all the time. However, for your purposes, if you want correctness without learning a new library, copy may be your best bet.
=======
The way the conversation is evolving now is that the person who asked the question just wants to have an input that will do something if it has certain fields. This sounds like a job for inheritance.
Suppose I have lots of events.
trait Event
I want to have a function that only does something if my event has actorId and teamId.
trait IdEvent {
def teamId: String
def actorId: String
} extends Event
Here is my function, that only does something if my event is an IdEvent
def processEvent(e: Event): Option[Int] = {
e match {
case event: IdEvent => someProcess(event)
case event: Event => None
}
}
where someProcess has signature
def someProcess(input: IdEvent): Option[Int]
I can't derive a case class with many attributes. Strangely the output varies between SBT and gradle. A minimal reproducible example is found at https://github.com/geoHeil/pureconfig-issue, also the code is listed below:
sbt
sbt compile
[error] Caused by: java.lang.ClassNotFoundException: scala.runtime.LazyRef
gradle
./gradlew compileScala
ould not find implicit value for parameter reader: pureconfig.ConfigReader[Foo.XXX]
pureconfig.ConfigReader[XXX]
for code of:
object Foo extends App {
println("here")
case class MyNestedThing(foo: String)
case class XXX(
a: String,
b: String,
c: String,
d: String,
e: String,
f: String,
g: String,
h: String,
i: String,
j: String,
k: String,
l: String,
m: String,
n: String,
o: String,
p: String,
q: String,
r: String,
s: String,
t: String,
u: String,
v: String,
w: String,
x: String,
y: String,
z: String,
aa: String,
ab: String,
ac: String,
ad: String,
ae: String,
af: String,
ag: String,
ah: String,
ai: String,
aj: String,
ak: String,
al: String,
am: String,
an: String,
ao: String,
ap: String,
aq: String,
someLonglllllllllllllllllllllllllll: String,
so1meLonglllllllllllllllllllllllllll: String,
som2eLonglllllllllllllllllllllllllll: String,
ar: MyNestedThing,
as: MyNestedThing
)
pureconfig.ConfigReader[XXX]
}
Though works just fine when using:
object Foo extends App {
println("here")
case class MyNestedThing(foo: String)
case class XXX(
a: String,
b: String,
som2eLonglllllllllllllllllllllllllll: String,
ar: MyNestedThing,
as: MyNestedThing
)
pureconfig.ConfigReader[XXX]
}
edit
interestingly, I had a mixup in scala versions so when fixing it to be all of 2.11 SBT compiles fine, however gradle still shows the same problem.
When giving more memory to the scala compiler it works fine.
https://github.com/pureconfig/pureconfig/issues/391
tasks.withType(ScalaCompile) {
configure(scalaCompileOptions.forkOptions) {
jvmArgs = ["-Xss2m"]
}
}
I have a case class:
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
value: Option[String]) {
}
This was working fine until I have a new use case where "value" parameter can be a class Object instead of String.
My initial implementation to handle this use case:
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String],
addressValue: Option[MailingAddress]) {
#JsonProperty("value")
def setAddressId(addressId: String): Unit = {
val this.`addressId` = Option(addressId)
}
def this(addressFormat: String, screeningAddressType: String, addressId: String) = {
this(addressFormat, screeningAddressType, Option(addressId), None)
}
def this(addressFormat: String, screeningAddressType: String, address: MailingAddress) = {
this(addressFormat, screeningAddressType, None, Option(address))
}
}
but I don't feel this is a good approach and it might create some problem in future.
What are the different ways I can accomplish the same?
Edit: Is there a way I can create a class containing three parameters: ** addressFormat, screeningAddressType, value** and handle both the use cases?
You do not need to provide auxilliary constructors here and neither the setter. You could simply use the copy method provided by the case class.
For example:
case class MailingAddress(email:String)
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String],
addressValue: Option[MailingAddress])
scala> val y = EvaluateAddress("abc", "abc", None, None)
y: EvaluateAddress = EvaluateAddress(abc,abc,None,None)
scala> y.copy(addressId = Some("addressId"))
res0: EvaluateAddress = EvaluateAddress(abc,abc,Some(addressId),None)
You can have a default value for fields in a case class.
So you can have the Optional fields default to None :
case class EvaluateAddress(addressFormat: String,
screeningAddressType: String,
addressId: Option[String] = None,
addressValue: Option[MailingAddress] = None)
Then when you create a new instance of EvaluateAddress, you can choose to pass a value for either of addressId, or addressValue or both ..or nothing at all.
I have a lot of similar case classes which mean different things but have the same argument list.
object User {
case class Create(userName:String, firstName: String, lastName: String)
case class Created(userName:String, firstName: String, lastName: String)
}
object Group {
case class Create(groupName:String, members: Int)
case class Created(groupName:String, members: Int)
}
Given this kind of a setup, I was tired of writing methods that take an argument of type Create and return an argument of type Created. I have tons of test cases that do exactly this kind of thing.
I could write a function to convert one case class into the other. This function converts User.Create into User.Created
def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create"))
I had to write another such function for Group.
What I'd really like to have is a generic function that takes the two types of the case classes and an object of one case class and converts into the other. Something like,
def transform[A,B](a: A):B
Also, this function shouldn't defeat the purpose of reducing boilerplate. Please feel free to suggest a different signature for the function if that's easier to use.
Shapeless to the rescue!
You can use Shapeless's Generic to create generic representations of case classes, that can then be used to accomplish what you're trying to do. Using LabelledGeneric we can enforce both types and parameter names.
import shapeless._
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
case class SortOfCreated(screenName: String, firstName: String, lastName: String)
val c = Create("username", "firstname", "lastname")
val createGen = LabelledGeneric[Create]
val createdGen = LabelledGeneric[Created]
val sortOfCreatedGen = LabelledGeneric[SortOfCreated]
val created: Created = createdGen.from(createGen.to(c))
sortOfCreatedGen.from(createGen.to(c)) // fails to compile
For the record, here is the simplest typesafe syntax I've managed to implement:
implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) {
def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
And it can be used like this:
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
val created = Create("foo", "bar", "baz").convertTo(Generic[Created])
Or the same thing with LabelledGeneric to achieve better type safety:
implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) {
def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created]))
I have the following case class definition:
case class FileMetadata(announceUrls: List[String], pieceLength: Int, pieces: String) {
case class SingleFileMetadata(filename: String, length: Int)
case class MultiFileMetadata(dirname: String, files: List[FileInfo])
}
because I want both the single and multi file types to have all the same data values as the enclosing FileMetaData case class. This doesn't really seem to work though (in that I can't create a new SingleFileMetadata or MultiFileMetaData class.
Is there a way to do this in Scala?
In your current code, SingleFileMetadata and MultiFileMetadata do not inherit from FileMetadata. In fact, they only exist within specific instances of FileMetadata, which you most certainly do not want. Furthermore, you cannot extend a case class, as all of the members are private, so FileMetadata would have to be a class (or trait).
Something like this might work for you:
class FileMetadata(announceUrls: List[String], pieceLength: Int, pieces: String)
case class SingleFileMetadata(filename: String, length: Int, announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata(announceUrls, pieceLength, pieces)
case class MultiFileMetadata(dirname: String, files: List[FileInfo], announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata(announceUrls, pieceLength, pieces)
You can make your base class a trait and make both case classes inherit from it.
trait FileMetadata {
def announceUrls: List[String]
def pieceLength: Int
def pieces: String
}
case class SingleFileMetadata(filename: String, length: Int, announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata
case class MultiFileMetadata(dirname: String, files: List[FileInfo], announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata
One additional advantage of using trait is that you can limit the inheritance to only the classes defined in the given file, by specifying that trait as a sealed trait.
Alternative solution to your problem would be to create only one generic case class, set optional parameters to default value None, with flexible pattern matching you can easily perform operation on this class.
case class GenericFileMetadata(filename:Option[String] = None,
length:Option[String] = None,
dirname:Option[String] = None,
files:Option[List[FileInfo]]= None,
announceUrls: List[String], pieceLength: Int, pieces: String)