3

There is a trait which works perfectly. However, I would like to refactor the part related to generic [T] in order to limit the data type which could be accepted by generic [T] (I need only Option[JsValue] , JsValue , StringEnumEntry , String ). Is it possible to solve this problem through shapeless coproduct? Maybe there are other solutions?

trait ParameterBinders extends Log {

  def jsonBinder[T](json: T, jsonType: java.lang.String = "json"): ParameterBinderWithValue = {
    val jsonObject = new PGobject()
    jsonObject.setType(jsonType)
    json match {
      case json: Option[JsValue] =>
        jsonObject.setValue(json.map(Json.stringify).orNull)
      case json: JsValue =>
        jsonObject.setValue(Json.stringify(json))
      case json: StringEnumEntry =>
        jsonObject.setValue(json.value)
      case json: String =>
        jsonObject.setValue(json)
      case _ =>
        logger.error("unexpected data type ")
    }
    if (jsonType == "JSONSCHEMATYPE" || jsonType == "SYSPROPERTYTYPE") {
      ParameterBinder(this, (ps, i) => {
        ps.setObject(i, jsonObject)
      })
    } else {
      ParameterBinder(json, (ps, i) => {
        ps.setObject(i, jsonObject)
      })
    }

  }
}
Tim Florian
  • 408
  • 1
  • 3
  • 13
  • I came across this issue before, may be this will help you - https://stackoverflow.com/questions/43495047/scala-variable-with-multiple-types – Ra Ka Jul 02 '18 at 10:00

1 Answers1

2

The easiest way is to use an ADT as described in the link of the first comment. If you don't want to change the types that are accepted in jsonBinder then you can solve the problem by using a typeclass.

e.g.

trait JsonBindValue[T] {
    def value(t: T): String
}

you would then have to provide instances for your accepted datatypes

object JsonBindValue {
    implicit val OptJsBinder = new JsonBindValue[Option[JsValue]] {
        def value(t: Option[JsValue]): String = {
            t.map(Json.stringify).orNull
        }
    }
   ... more instances here
}

finally your function would look like this:

def jsonBinder[T : JsonBindValue](json: T, jsonType: java.lang.String = "json"): ParameterBinderWithValue = {
    val binder = implicitly[JsonBindValue[T]]
    jsonObject.setType(jsonType)
    jsonObject.setValue(binder.value(json))
    ...
}

if you call the function without a implicit instance in scope you will get a compile time error.

fusion
  • 1,257
  • 10
  • 15
  • Nice answer. Its just that these types do not qualify as ADT's. So it will be great, if we just replace ADT by `type class` and remove that piece of mis-information. – sarveshseri Jul 02 '18 at 10:23