如何从String中提取值以在Scala中创建案例类实例

时间:2015-11-25 12:08:08

标签: scala functional-programming

我正在尝试从标记化的String中提取值,并从中创建一个(可选的)case类实例。

String采用以下形式:

val text = "name=John&surname=Smith"

我有一个Person类,它接受这两个值:

case class Person(name: String, surname: String)

我有一些代码可以进行转换:

def findKeyValue(values: Array[String])(prefix: String): Option[String] = 
     values.find(_.startsWith(prefix)).map(_.substring(prefix.length)) 

val fields: Array[String] = text.split("&")
val personOp = for {
   name <- findKeyValue(fields)("name=")
   surname <- findKeyValue(fields)("surname=")
} yield Person(name, surname)

虽然这产生了我需要的答案但我想知道:

  1. 有更有效的方法吗?
  2. 有更多以功能编程为中心的方法吗?
  3. 一些限制:

    1. 文本中名称和姓氏字段的顺序可以更改。以下内容也有效:

      val text = "surname=Smith&name=John"
      
    2. 可能还有其他字段需要忽略:

      val text = "surname=Smith&name=John&age=25"
      
    3. 解决方案需要满足提供的文本格式错误或没有必填字段的时间。

    4. 解决方案无法使用反射或宏。

3 个答案:

答案 0 :(得分:2)

如果你在开始时将它一直解析为Map[String,String](而不是Array[String],那么它会更有效率。

如果您碰巧将apache的http-client库作为依赖项的一部分(如果您使用的是Web框架,那么很有可能),我会使用它:

import org.apache.http.client.utils.URLEncodedUtils
import java.nio.charset.StandardCharsets
import scala.collection.JavaConverters._

val values = URLEncodedUtils.parse(text, StandardCharsets.UTF_8)
  .asScala.map(x => x.getName -> x.getValue).toMap

val personOpt = 
  for {
    name <- values.get("name")
    surname <- values.get("surname")
  } yield Person(name, surname)

使用库的原因是假设这是来自各种类型的http请求,您很可能需要对键以及库所处理的值或其他详细信息进行urldecode。

我认为提取器版本会过度,但这就是它的样子:

object PersonFromString {
  def unapply (s: String): Option[Person] = { ... same as above ... }
}
...
text match {
  case PersonFromString(person) => ... do something with it...
  ...
}

答案 1 :(得分:0)

我想说,做这些事情的更惯用的方法是使用Extractors

考虑这个答案:Read case class object from string in Scala (something like Haskell's "read" typeclass)

答案 2 :(得分:0)

在解析字符串时,从属性到值构造Map[String,Option[String]],例如

val m = text.split("&")
            .map(_.split("="))
            .filter(_.size == 2)
            .map (xs => xs.head -> Some(xs.last))
            .toMap

获取例如

Map(surname -> Some(Smith), name -> Some(John))

要获取构造类实例的值(例如,已经提供的提取器),请像这样使用getOrElse

m.getOrElse("kjkj",None)
Option[String] = None

m.getOrElse("surname",None)
Option[String] = Some(Smith)

案例类需要重新制定为

case class Person(name: Option[String], surname: Option[String])

每当字符串不包含name中不存在的此类属性/值时,surnameNone可能为Map。另请注意,为了传达大小为2的数组的过滤,可以使用模式匹配。