How to make only few datatype which is not related to each other acceptable by generics

84 Views Asked by At

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)
      })
    }

  }
}
1

There are 1 best solutions below

1
fusion On BEST ANSWER

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.