Reputation: 2572
When serializing the following case class, the val element is not included. Why is that, and can I have it included?
case class Asset(id: Option[Int], description: Option[String]= None) {
val url = "images/" + id.toString+".png"
}
Update: Added Json library, and specification/intended use of the url "property".
I am using the Json library shipped with Play 2.1/Scala 2.10.
Actually the url property is meant to be a function which will look up the transformation algorithm according to configuration, for example the image can be available locally, or available from an external host.
Upvotes: 3
Views: 193
Reputation: 32719
Although you sould really specify which json serialization library you are using, it is pretty much guaranteed that just doing the following will work:
case class Asset(id: Option[Int], description: Option[String]= None, url = "images/" + id.toString+".png")
Given that url
has a default value anyway, turning it into a parameter won't negatively affect your code (you can still do Asset(None)
by example, as before). The only downside is that now client code might create an Asset
instance with a different value for url
, which might not be what you want.
If that's the case, you'll probably need to create a custom json format for your Asset
class, but without knowing which serialization library you use I can't help much more in this respect.
UPDATE:
Ooops, I totally missed the fact that the default value for url
depends on another parameter (id
) (thanks to @Kristian Domagala for noticing). Thus my above snippet does not compile. One simple solution is to put url
in a second parameter list as suggested by @Kristian Domagala:
case class Asset(id:Option[Int],description:Option[String]=None)(val url:String = "images/" + id.toString+".png")
But this might not be ideal as this changed the equality semantics of Asset
(url
is not taken into account anymore when comparing instances), and also changes the construction syntax : when explicitly specifying the url
value, you'd need to do something like Asset(Some(123))("gfx/myImage.png")
instead of by example Asset(Some(123), url="gfx/myImage.png")
.
If you can live with these downsides this is certainly the easiest solution.
Otherwise, there is another work around: we can redefine Asset.apply
ourselve (by hand):
case class AssetImpl( val id: Option[Int], val description: Option[String], val url: Option[String]) {
override def productPrefix = "Asset"
}
type Asset = AssetImpl
object Asset {
def apply( id: Option[Int], description: Option[String] = None, url: Option[String] = None ) = {
new Asset( id, description, url.orElse( id.map( "images/" + _ + ".png") ) )
}
}
As you can see I have turned url
into an Option
with a default value of None
(avoiding the previous compilation error, as it does not depend on id
anymore) due to , and in def apply...
I instantiate Asset
with a default value for url
(this is where I actually get the value of id
) of id.map( "images/" + _ + ".png")
.
The rest is basically just noise to be able to redefine Asset.apply
: it turns out that in fact you cannot redefine the factory of a case class (you can only add separate overloads). So I renamed the class as AssetImpl
, added a type alias so that noone notices ( ;-) ), and created my very own object Asset
where I define the apply
method (which does not conflict anymore with the auto-generated apply
method because this one is in the distinct AssetImpl
object.
I could also simply have turned Asset
into a standard class (non case class) but then I would have to redefine equals
and hashCode
which I find more annoying given that it must be maintained when adding/removing fields to the class.
Upvotes: 2