How to convert sealed trait case objects to string using circe - scala

I am using Scala and Circe. I have the following sealed trait.
sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode
The output of this case object when called SessionModel.Authentication is the following:
"Authentication":{}
I need to convert this to a string so it outputs "authentication"

As Andriy Plokhotnyuk notes above, you can use circe-generic-extras:
import io.circe.Codec
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveEnumerationCodec
sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode
object Mode {
private implicit val config: Configuration =
Configuration.default.copy(transformConstructorNames = _.toLowerCase)
implicit val modeCodec: Codec[Mode] = deriveEnumerationCodec[Mode]
}
And then:
scala> import io.circe.syntax._
import io.circe.syntax._
scala> (Authentication: Mode).asJson
res1: io.circe.Json = "authentication"
scala> io.circe.Decoder[Mode].decodeJson(res1)
res2: io.circe.Decoder.Result[Mode] = Right(Authentication)
(Note that Codec is new in 0.12—for earlier versions you'll have to write out both instances as in Andriy's comment.)
Unless you have a lot of these to maintain, though, I personally think writing the instances out by hand is often better than using circe-generic-extras, and in this case it's not even much more verbose:
import io.circe.{Decoder, Encoder}
sealed trait Mode
case object Authentication extends Mode
case object Ocr extends Mode
object Mode {
implicit val decodeMode: Decoder[Mode] = Decoder[String].emap {
case "authentication" => Right(Authentication)
case "ocr" => Right(Ocr)
case other => Left(s"Invalid mode: $other")
}
implicit val encodeMode: Encoder[Mode] = Encoder[String].contramap {
case Authentication => "authentication"
case Ocr => "ocr"
}
}
Which works exactly the same as the deriveEnumerationCodec version but doesn't require anything but circe-core, is less magical, compiles much faster, etc. Generic derivation can be great for simple case classes with straightforward mappings, but I think people too often try to stretch it to cover all cases when writing instances manually wouldn't be much of a burden and might even be clearer.

Related

How do I get circe to have an either/or output scenario for the generated Json?

Here's what I intend - let's say I have a field called medical_payments - it can "either" be a limit if one elects or waived
{
"medical_payments":
{
"limit_value":"one_hundred"
}
}
If it's elected as a waiver then it should be:
{
"medical_payments":
{
"waived":true
}
}
So far here's what I have:
sealed trait LimitOrWaiver
case class Limit(limit_key: String) extends LimitOrWaiver
case class Waived(waived: Boolean) extends LimitOrWaiver
case class Selection(medical_payments: LimitOrWaiver)
Sample data:
Selection(medical_payments = Limit("one_hundred")).asJson
Output:
{
"medical_payments":
{
"Limit": { "limit_value":"one_hundred" } // additional object added
}
}
Similarly for Selection(medical_payments = Waived(true)).asJson an additional Waived:{...} is added to the Json.
I'd like it to be an either/or. What's the best way to achieve this?
The only way that I've been able to think of (not to my liking) is to use forProductN functions per the doc and manually do all this - but it's way to cumbersome for a large Json.
You can almost accomplish this with generic derivation using the configuration in generic-extras:
sealed trait LimitOrWaiver
case class Limit(limitValue: String) extends LimitOrWaiver
case class Waived(waived: Boolean) extends LimitOrWaiver
case class Selection(medicalPayments: LimitOrWaiver)
import io.circe.generic.extras.Configuration, io.circe.generic.extras.auto._
import io.circe.syntax._
implicit val codecConfiguration: Configuration =
Configuration.default.withDiscriminator("type").withSnakeCaseMemberNames
And then:
scala> Selection(medicalPayments = Limit("one_hundred")).asJson
res0: io.circe.Json =
{
"medical_payments" : {
"limit_value" : "one_hundred",
"type" : "Limit"
}
}
(Note that I've also changed the Scala case class member names to be Scala-idiomatic camel-case, and am handling the transformation to snake-case in the configuration.)
This isn't exactly what you want, since there's that extra type member, but circe's generic derivation only supports round-trip-able encoders / decoders, and without a discriminator of some kind—either a member like this or the extra object layer you point out in the question—it's impossible to round-trip values of arbitrary ADTs through JSON.
This might be fine—you might not care about the extra type in your object. If you do care, you can still use derivation with a little extra work:
import io.circe.generic.extras.Configuration, io.circe.generic.extras.auto._
import io.circe.generic.extras.semiauto._
import io.circe.ObjectEncoder, io.circe.syntax._
implicit val codecConfiguration: Configuration =
Configuration.default.withDiscriminator("type").withSnakeCaseMemberNames
implicit val encodeLimitOrWaiver: ObjectEncoder[LimitOrWaiver] =
deriveEncoder[LimitOrWaiver].mapJsonObject(_.remove("type"))
And:
scala> Selection(medicalPayments = Limit("one_hundred")).asJson
res0: io.circe.Json =
{
"medical_payments" : {
"limit_value" : "one_hundred"
}
}
If you really wanted you could even make this automatic, so that type would be removed from any ADT encoders you derive.

Fail-fast json4s serialisation of sealed trait and object enum when missing serializer

Set up
I'm using json4s 3.2.11 and Scala 2.11.
I have an enumeration defined using sealed trait, and a custom serializer for it:
import org.json4s.CustomSerializer
import org.json4s.JsonAST.JString
import org.json4s.DefaultFormats
import org.json4s.jackson.Serialization
sealed trait Foo
case object X extends Foo
case object Y extends Foo
object FooSerializer
extends CustomSerializer[Foo](
_ =>
({
case JString("x") => X
case JString("y") => Y
}, {
case X => JString("x")
case Y => JString("y")
})
)
This is great, and works well when added to the formats:
{
implicit val formats = DefaultFormats + FooSerializer
Serialization.write(X) // "x"
}
This is great!
Problem
If the serializer is not added to the formats, json4s will use reflection to create a default representation of the fields, which is extremely unhelpful for these objects that don't have fields. It does this silently, seemingly without a way to control it.
{
implicit val formats = DefaultFormats
Serialization.write(X) // {}
}
This is a problematic, as there's no indication of what's gone wrong until much later. This invalid/useless data might be sent around the network or written to databases, if tests don't happen to catch it. And, this may be exposed publicly from a library, meaning downstream users have to remember it as well.
NB. this is different to read, which throws an exception on failure, since the Foo trait doesn't have any useful constructors:
{
implicit val formats = DefaultFormats
Serialization.read[Foo]("\"x\"")
}
org.json4s.package$MappingException: No constructor for type Foo, JString(x)
at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$constructor(Extraction.scala:417)
at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$instantiate(Extraction.scala:468)
at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$result$6.apply(Extraction.scala:515)
...
Question
Is there a way to either disable the default {} formatting for these objects, or to "bake" in the formatting to the object itself?
For instance, having write throw an exception like read would be fine, as it would flag the problem to the caller immediately.
There is an old open issue which seems to ask similar question where one of the contributors suggests to
you need to create a custom deserializer or serializer
which makes it sound there is no out-of-the-box way to alter the default behaviour.
Method 1: Disallow default formats via Scalastyle
Try disallowing import of org.json4s.DefaultFormats using Scalastyle IllegalImportsChecker
<check level="error" class="org.scalastyle.scalariform.IllegalImportsChecker" enabled="true">
<parameters>
<customMessage>Import from illegal package: Please use example.DefaultFormats instead of org.json4s.DefaultFormats</customMessage>
<parameter name="illegalImports"><![CDATA[org.json4s.DefaultFormats]]></parameter>
</parameters>
</check>
and provide custom DefaultFormats like so
package object example {
val DefaultFormats = Serialization.formats(NoTypeHints) + FooSerializer
}
which would allow us to serialise ADTs like so
import example.DefaultFormats
implicit val formats = DefaultFormats
case class Bar(foo: Foo)
println(Serialization.write(Bar(X)))
println(Serialization.write(X))
println(Serialization.write(Y))
which should output
{"foo":"x"}
"x"
"y"
If we try to import org.json4s.DefaultFormats, then Scalastyle should raise the following error:
Import from illegal package: Please use example.DefaultFormats instead of org.json4s.DefaultFormats
Method 2: Bake in serialisation for non-nested values
Perhaps we could "bake in" the formatting into objects by defining write method in Foo which delegates to Serialization.write like so
sealed trait Foo {
object FooSerializer extends CustomSerializer[Foo](_ =>
({
case JString("x") => X
case JString("y") => Y
}, {
case X => JString("x")
case Y => JString("y")
})
)
def write: String =
Serialization.write(this)(DefaultFormats + FooSerializer)
}
case object X extends Foo
case object Y extends Foo
Note how we hardcoded passing FooSerializer format to write. Now we can serialise with
println(X.write)
println(Y.write)
which should output
"x"
"y"
Method 3: Provide custom DefaultFormats alongside org.json4s.DefaultFormats
We could also try defining custom DefaultFormats in our own package like so
package example
object DefaultFormats extends DefaultFormats {
override val customSerializers: List[Serializer[_]] = List(FooSerializer)
}
which would allow us to serialise ADTs like so
import example.DefaultFormats
implicit val formats = DefaultFormats
case class Bar(foo: Foo)
println(Serialization.write(Bar(X)))
println(Serialization.write(X))
println(Serialization.write(Y))
which should output
{"foo":"x"}
"x"
"y"
Having two default formats, org.json4s.DefaultFormats and example.DefaultFormats, would at least make the user have to choose between the two, if say, they use IDE to auto-import them.

Scala shapeless implicit resolution StackOverflowError with ArgonautShapeless

Im using ArgonautShapeless to define some json codecs.
When I provide the type for my codec I get StackOverflowError's but If I leave the type off it works. How can I provide the type?
My understanding of the problem is that the implicit lookup from def of[A: DecodeJson] = implicitly[DecodeJson[A]] finds my definition on the same line implicit def fooCodec: DecodeJson[Foo] and thus is recursive so breaks.
Is there some other way that will allow me to provide the type? Ideally I want to have one object in my project where I define all of the codes and they may depend on each other.
import $ivy.`com.github.alexarchambault::argonaut-shapeless_6.2:1.2.0-M4`
import argonaut._, Argonaut._
case class Foo(a: Int)
object SomeCodecs {
import ArgonautShapeless._
// this doesnt work
implicit def fooCodec: DecodeJson[Foo] = DecodeJson.of[Foo]
}
import SomeCodecs._
"""{"a":1}""".decode[Foo]
java.lang.StackOverflowError
ammonite.$sess.cmd3$SomeCodecs$.fooCodec(cmd3.sc:3)
It works if I leave the type off.
object SomeCodecs {
import ArgonautShapeless._
// this works
implicit def fooCodec = DecodeJson.of[Foo]
}
import SomeCodecs._
"""{"a":1}""".decode[Foo]
res4: Either[Either[String, (String, CursorHistory)], Foo] = Right(Foo(1))
Thanks

scala-json unserialize in scala.js

I'm trying to convert JSON from Ajax request to Case class in Scala.js using scala-json https://github.com/MediaMath/scala-json
Here is my classes:
sealed trait Result
sealed trait Error extends Result
sealed trait Msg extends Result
case class MsgData(msg: Seq[String], args: Seq[Int]) extends Msg
case class CommentError(#name("obj.comment") comment: Seq[MsgData]) extends Error
Here is how I'm trying to convert:
import json._
implicit val msgDataAcc = ObjectAccessor.create[MsgData]
implicit val commentErrorAcc = ObjectAccessor.create[CommentError]
println("here2")
val errors = JValue.fromString(req.responseText).toObject[CommentError]
println("here3")
This code just silently dies on string with conversion and "here3" never printed to console.
Here is my JSON from server:
{"obj.comment":[{"msg":["error.minLength"],"args":[10]}],"obj.name":[{"msg":["error.path.missing"],"args":[]}]}
What I'm doing wrong? How to fix this?
So I'm guessing this is scala-js. Any exceptions that happen at the top level (entry point) of a scala-js application aren't always echoed out correctly (depending on environment/browser), if you wrap the whole thing in a Try and print out the stack trace during the catch, you should successfully see exception being thrown.
Main issue above is that you need to define 'accessors' for the case classes. There's 2 ways of doing this, one works out of the box by adding an implicit for each type, the other way requires macro-paradise and gives you a much simpler way of defining accessors for case classes.
Here is the normal non-macro-paradise way:
case class MsgData(msg: Seq[String], args: Seq[Int]) extends Msg
object MsgData {
implicit val acc = ObjectAccessor.create[MsgData]
}
case class CommentError(#name("obj.comment") comment: Seq[MsgData]) extends Error
object CommentError {
implicit val acc = ObjectAccessor.create[CommentError]
}
The implicits can be placed anywhere (following the general rule for scala implicits). Placing them in the companion object is the best way to guarantee the implicit can be found anywhere, without special imports or anything needed.
This is 'less magical' than other libs like circe that use shapeless to automatically derive factories, sometimes in a bloated way. scala-json aims to keep the accessor visible for extension purposes, but this does lead to some explicit boilerplate.
This can be reduced down using macro-paradise:
#accessor case class MsgData(msg: Seq[String], args: Seq[Int]) extends Msg
#accessor case class CommentError(#name("obj.comment") comment: Seq[MsgData]) extends Error
This does the exact same thing as the above code, we just leverage macro-paradise to add the implicit 'acc' field to the companion object automatically.

Custom Scala enum, most elegant version searched

For a project of mine I have implemented a Enum based upon
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
from Case objects vs Enumerations in Scala. I worked quite nice, till I run into the following problem. Case objects seem to be lazy and if I use Currency.value I might actually get an empty List. It would have been possible to make a call against all Enum Values on startup so that the value list would be populated, but that would be kind of defeating the point.
So I ventured into the dark and unknown places of scala reflection and came up with this solution, based upon the following SO answers. Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
and How can I get the actual object referred to by Scala 2.10 reflection?
import scala.reflect.runtime.universe._
abstract class Enum[A: TypeTag] {
trait Value
private def sealedDescendants: Option[Set[Symbol]] = {
val symbol = typeOf[A].typeSymbol
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
else None
}
def values = (sealedDescendants getOrElse Set.empty).map(
symbol => symbol.owner.typeSignature.member(symbol.name.toTermName)).map(
module => reflect.runtime.currentMirror.reflectModule(module.asModule).instance).map(
obj => obj.asInstanceOf[A]
)
}
The amazing part of this is that it actually works, but it is ugly as hell and I would be interested if it would be possible to make this simpler and more elegant and to get rid of the asInstanceOf calls.
Here is a simple macro based implementation:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
abstract class Enum[E] {
def values: Seq[E] = macro Enum.caseObjectsSeqImpl[E]
}
object Enum {
def caseObjectsSeqImpl[A: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
val typeSymbol = weakTypeOf[A].typeSymbol.asClass
require(typeSymbol.isSealed)
val subclasses = typeSymbol.knownDirectSubclasses
.filter(_.asClass.isCaseClass)
.map(s => Ident(s.companion))
.toList
val seqTSymbol = weakTypeOf[Seq[A]].typeSymbol.companion
c.Expr(Apply(Ident(seqTSymbol), subclasses))
}
}
With this you could then write:
sealed trait Currency
object Currency extends Enum[Currency] {
case object USD extends Currency
case object EUR extends Currency
}
so then
Currency.values == Seq(Currency.USD, Currency.EUR)
Since it's a macro, the Seq(Currency.USD, Currency.EUR) is generated at compile time, rather than runtime. Note, though, that since it's a macro, the definition of the class Enum must be in a separate project from where it is used (i.e. the concrete subclasses of Enum like Currency). This is a relatively simple implementation; you could do more complicated things like traverse multilevel class hierarchies to find more case objects at the cost of greater complexity, but hopefully this will get you started.
A late answer, but anyways...
As wallnuss said, knownDirectSubclasses is unreliable as of writing and has been for quite some time.
I created a small lib called Enumeratum (https://github.com/lloydmeta/enumeratum) that allows you to use case objects as enums in a similar way, but doesn't use knownDirectSubclasses and instead looks at the body that encloses the method call to find subclasses. It has proved to be reliable thus far.
The article "“You don’t need a macro” Except when you do" by Max Afonov
maxaf describes a nice way to use macro for defining enums.
The end-result of that implementation is visible in github.com/maxaf/numerato
Simply create a plain class, annotate it with #enum, and use the familiar val ... = Value declaration to define a few enum values.
The #enum annotation invokes a macro, which will:
Replace your Status class with a sealed Status class suitable for acting as a base type for enum values. Specifically, it'll grow a (val index: Int, val name: String) constructor. These parameters will be supplied by the macro, so you don't have to worry about it.
Generate a Status companion object, which will contain most of the pieces that now make Status an enumeration. This includes a values: List[Status], plus lookup methods.
Give the above Status enum, here's what the generated code looks like:
scala> #enum(debug = true) class Status {
| val Enabled, Disabled = Value
| }
{
sealed abstract class Status(val index: Int, val name: String)(implicit sealant: Status.Sealant);
object Status {
#scala.annotation.implicitNotFound(msg = "Enum types annotated with ".+("#enum can not be extended directly. To add another value to the enum, ").+("please adjust your `def ... = Value` declaration.")) sealed abstract protected class Sealant;
implicit protected object Sealant extends Sealant;
case object Enabled extends Status(0, "Enabled") with scala.Product with scala.Serializable;
case object Disabled extends Status(1, "Disabled") with scala.Product with scala.Serializable;
val values: List[Status] = List(Enabled, Disabled);
val fromIndex: _root_.scala.Function1[Int, Status] = Map(Enabled.index.->(Enabled), Disabled.index.->(Disabled));
val fromName: _root_.scala.Function1[String, Status] = Map(Enabled.name.->(Enabled), Disabled.name.->(Disabled));
def switch[A](pf: PartialFunction[Status, A]): _root_.scala.Function1[Status, A] = macro numerato.SwitchMacros.switch_impl[Status, A]
};
()
}
defined class Status
defined object Status