lift-json extract question

20 views
Skip to first unread message

Colin Bester

unread,
May 27, 2017, 9:09:59 AM5/27/17
to Lift
I am in process of adding Kafka to my LiftWeb project. I have several topic case classes and my KafkaConsumer subscribes to a set of topics. Maybe overkill, but I prefer deserializing received json string data in the KafkaConsumer process before passing onto my Actor for sending to subscribed CometActors etc.

One way I see doing this is as:

(please note these are not running snippets, just example code show intent)

trait TopicTrait
case class Topic1(name: String) extends TopicTrait
case class Topic2(role: Int) extends TopicTrait


//KafkaConsumer fetches topic and serialized json string (data) of either Topic1 or Topic2
def processConsumerData(Topic: String, data: String) {
   val json
= parse(data)
   
Topic match {
   
case "topic1" => {
     
//preference is to deserialize and not just send json string is to confirm json received is at least correct format
     val item
= json.extract[Topic1]
     
MyActor ! item
   
}
   
case "topic2" => {
     val item
= json.extract[Topic2]
     
MyActor ! item
   
}
 
}
}

But I don't like having to manage the pattern case like this.

Is there a way to do something along lines of snippet below or other suggestions. This way I could dynamically build a mapping function of topic names to topic classes but my mind has gone into 'slow mode' and vicious downward spiral, so suggestions much appreciated.

trait TopicTrait
case class Topic1(name: String)
case class Topic2(role: Int)


//I'd like to be able to create map or list of topic name to topic class, eg:
val topicMapper
= Map[String, TopicTrait]("topic1" -> *what goes here* Topic1, "topic2" -> Topic2)


def processConsumerData(topic: String,  data: String) {
 val json
= parse(data)
 val item
= json.extract[*what goes here*topicMapper.get(topic).get] //I know .get is bad
 
MyActor ! item
}

Matt Farmer

unread,
May 27, 2017, 11:19:52 AM5/27/17
to Lift
Yep, something like this is certainly possible.

You'll likely want to define a map like this:

val topicMapper: Map[String, Manifest[_]] = Map(
  "topic1" -> manifest[Topic1],
  "topic2" -> manifest[Topic2]
)

Then when you can do something like:

import net.liftweb.json.Extraction._

topicMapper.get(topic) match {
  case Some(topicManifest) =>
    extract(json, topicManifest)

  case None =>
    // No matching thing
}

That should give you what you want.

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Colin Bester

unread,
May 27, 2017, 11:55:13 AM5/27/17
to Lift
Matt, thanks for fast response and good to know I wasn't far off but didn't think of Manifest[_], I was trying TypeTag[MyTopic] and other variations but not underscore.

When trying this however I still get error

topicMapper.get(topic) match {
 case Some(topicManifest) =>
   extract(json, topicManifest)
 case None =>
   // No matching thing
}

extract(json, topicManifest) shows error:
type mismatch; found : scala.reflect.Manifest[_$1] where type _$1 required: net.liftweb.json.TypeInfo.

I'll dig in some more but thought I'd post and see if it kicks any quick thoughts.

Thanks again!

Colin Bester

unread,
May 27, 2017, 12:12:38 PM5/27/17
to Lift
Code below works for me but I don't really understand use of TypeInfo and am not sure if being used correctly here (I am simply passing None for parameterizedType.

trait MyTopics

case class MyTopic1(name: String) extends MyTopics
case class MyTopic2(age: Int) extends MyTopics

object Topic2Class extends App {

 
implicit val formats = net.liftweb.json.DefaultFormats

  val topicMapper
: Map[String, TypeInfo] = Map(
     
"topic1" -> TypeInfo(classOf[MyTopic1], None),
     
"topic2" -> TypeInfo(classOf[MyTopic2], None)
 
)
 
  val topic1Json
= """{"name":"Bob"}"""
  val topic2Json
= """{"age":20}"""

// val topic = "topic1"
// val json = parse(topic1Json)

  val topic
= "topic2"
  val json
= parse(topic2Json)

  topicMapper
.get(topic) match {
   
case Some(topicManifest) =>
      val res
= extract(json, topicManifest)
      println
(s"res $res")
   
case None =>
      println
("No match")
 
}
}


Antonio Salazar Cardozo

unread,
May 28, 2017, 8:57:26 AM5/28/17
to Lift
You can go back to the `Manifest` approach and use extract(jvalue)(formats, manifest). You'll have to explicitly pass the manifest is all.

Another option is to leverage lift-json's type extraction directly by using `ShortTypeHints` or your own TypeHints subclass that maps a topic name to a class. Then you could do e.g.:

extract(myJson ~ ("jsonClass" -> topic))

That feels maybe slightly cleaner, but maybe not heh.
Thanks,
Antonio

Reply all
Reply to author
Forward
0 new messages