Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easily parse String of Key=Value pairs to Scala case class

Is there any way to easily parse a string of key value pairs into a scala case class?

For example from the following string:

"consumer_key=1234ABC, consumer_secret=12345ABC"

into

case class Auth(consumerKey: String, consumerSecret: String)
like image 889
Anthony McCormick Avatar asked Oct 25 '12 13:10

Anthony McCormick


3 Answers

You can use regex and pattern matching:

scala> val R = "consumer_key=(.*), consumer_secret=(.*)".r
R: scala.util.matching.Regex = consumer_key=(.*), consumer_secret=(.*)

scala> "consumer_key=1234ABC, consumer_secret=12345ABC" match {
     |   case R(k, v) => Auth(k, v)
     | }
res0: Auth = Auth(1234ABC,12345ABC)

Use JavaTokenParsers for more flexible parsing:

import scala.util.parsing.combinator._

case class Auth( consumerKey: String, consumerSecret: Option[String])

class AuthParser extends JavaTokenParsers {
  def auth: Parser[Auth] = key ~ opt("," ~> secret) ^^ { case k ~ s => Auth(k, s)}
  def key: Parser[String] = value("consumer_key")
  def secret: Parser[String] = value("consumer_secret")
  def value(k: String): Parser[String] = k ~ "=" ~> "[^,]*".r
  def apply(s: String) = parseAll(auth, s)
}

Usage:

scala> val p = new AuthParser
p: AuthParser = AuthParser@433b9799

scala> p("consumer_key=1234ABC, consumer_secret=12345ABC").get
res0: Auth = Auth(1234ABC,Some(12345ABC))

scala> p("consumer_key=1234ABC").get
res1: Auth = Auth(1234ABC,None)
like image 109
senia Avatar answered Nov 07 '22 15:11

senia


I love the scala Parsers library, but it is a little slow. If your strings all look like that, you can just as easily do:

case class Auth( consumerKey:String, consumerSecret:String)
object Auth {
  def fromString(string: String): Seq[Auth] = string.split(", ").toSeq map { pair =>
    val lst = pair.split("=")
    Auth(lst(0), lst(1))
  }
}
like image 35
nnythm Avatar answered Nov 07 '22 15:11

nnythm


Starting Scala 2.13, if the pattern you're expecting is simple enough, it might be possible to pattern match it by unapplying a string interpolator:

// case class Auth(consumerKey: String, consumerSecret: String)
"consumer_key=1234ABC, consumer_secret=12345ABC" match {
  case s"consumer_key=$key, consumer_secret=$secret" => Auth(key, secret)
}
// Auth("1234ABC", "12345ABC")
like image 2
Xavier Guihot Avatar answered Nov 07 '22 17:11

Xavier Guihot