I have a custom Jackson deserializer written in scala. Structure is as follows.
override def deserialize(jp: JsonParser, ctx: DeserializationContext): Item = {
val node: JsonNode = jp.getCodec.readTree(jp)
// few double fields
val usageHours = node.get("usageHours").asDouble()
val usageUnits = node.get("usageUnits").asDouble()
// few string fields
val accountId = node.get("accountId").asText()
val accountName = node.get("accountName").asText()
// few long fields
val cardinality = node.get("cardinality").asLong()
// few date fields
val endDateTime = customDeserializer.convert(node.get("startDateTime").asText())
// few optionals with default values
val engine = Option(node.get("engine")).map(value => value.asText()).getOrElse(Constants.Unknown)
// case class object
Item(usageHours,
usageUnits,
accountId,
accountName,
cardinality,
endDateTime,
engine)
}
Problem is that there are too many fields to parse and obviously calling node.get() or Option(node.get) for each field is not good. I'm trying to write a generic method getValueForJsonKey() which can
- Parse key values and return them as either text, long, double, or date as required by the user.
- This method should also accept a default value which is optional. If any user provided key is missing in the JSON, default value will be returned.
Here's what i came up with
// Basically trying to identify the return type from the default value passed by the user
// This will not work if the user doesn’t pass a default value.
private def parseValueForJsonKey [A] (node: JsonNode, key: String, defaultValue: A): A = {
val parsedValue = Option(node.get(key)).map(value => {
defaultValue match {
case _: String => value.asText()
case _: Double => value.asDouble()
case _: Long => value.asLong()
case _: ZonedDateTime => customDateDeserializer.convert(value)
case _ => throw new RuntimeException(s"Unsupported type. Cannot parse ${key} to other than supported types")
}
})
parsedValue match{
case Some(value) => value.asInstanceOf[A]
case None => defaultValue
}
}
This clearly doesn't work and is not the right way to do it. I'm sure there are better ways to do this. Appreciate your help.
Revision 2
def parseValueForJsonKeyWithReturnType[A: TypeTag](
node: JsonNode,
key: String,
defaultValue: Any = None
): A = {
val parsedValue = Option(node.get(key)).map(value => {
typeOf[A] match {
case t if t =:= typeOf[String] =>
value.asText()
case t if t =:= typeOf[Double] =>
value.asDouble()
case t if t =:= typeOf[Long] =>
value.asLong()
case t if t =:= typeOf[ZonedDateTime] =>
zonedDateTimeDeserializer.convert(value.asText())
case _ => throw new RuntimeException(s"Parsing to ${typeOf[A]} isn't supported by custom deserializer")
}
})
parsedValue.getOrElse(defaultValue).asInstanceOf[A]
}