将Scala List[String]/List[Object] 转换成model/HList/tuple [英] Convert scala List[String]/List[Object] into model/HList/tuple

查看:56
本文介绍了将Scala List[String]/List[Object] 转换成model/HList/tuple的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

外部系统返回 Seq[String](一种 DB,输出如 CSV/json),它是基本类型的包装:字符串/数字.我宁愿使用我自己的模型.

An external system returns Seq[String] (kind of DB, output like CSV/json), it's wrap of base types: string/numbers. I'd rather work with my own model.

object Converter {
  type Output = (Int, String, Double) // for instance 
  def convert(values: List[String]): Output
}

显然,我不想每次都实现 convert-method.

Obviously, I do not want to implement convert-method every time.

似乎我需要一些比http://nrinaudo.github.io/tabulate/tut/parsing.html

这里可以使用HList吗?就像通过定义显式的输出类型将大小的 HList (String::String::String::HNil) 转换为模型一样.

Is it possible to use HList here? Like conversion sized HList (String:: String:: String:: HNil) into model by defining explicit only Output type.

推荐答案

首先,convert 方法的输出必须是 Option[Output],或者Output 的一些 monad (Try, Either, scalaz.\/, scalaz.Validation 等),以防 Seq[String] 的内容无法转换为 Output(Seq 的长度错误,解析 Ints 或 Double 等时出错)

First of all, the output of convert method would have to be Option[Output], or some monad of Output (Try, Either, scalaz.\/, scalaz.Validation, etc.) in case the contents of the Seq[String] can't be converted to Output (wrong length of Seq, errors in parsing Ints or Doubles, etc.)

一个可能的 shapeless 实现将有一个类型类来将 String 转换为其参数类型,以及一个辅助类型类来转换 HListHListString 到具有第一个类型类的 OutputHList 表示.

A possible implementation with shapeless would have a typeclass to convert String to its parameter type, and an auxiliary typeclass to convert an HList of String to the HList representation of Output with the first typeclass.

这是一个示例实现:

import shapeless._
import shapeless.syntax.std.traversable._
import shapeless.ops.traversable._

trait Parse[Out] {
  def apply(value: String): Option[Out]
}
object Parse {
  implicit object convertToInt extends Parse[Int] {
    def apply(value: String) = Try(value.toInt).toOption
  }

  implicit object convertToString extends Parse[String] {
    def apply(value: String) = Some(value)
  }

  implicit object convertToDouble extends Parse[Double] {
    def apply(value: String) = Try(value.toDouble).toOption
  }
}

trait ParseAll[Out] {
  type In <: HList
  def apply(values: In): Option[Out]
}

object ParseAll {

  type Aux[I, O] = ParseAll[O] { type In = I }

  implicit object convertHNil extends ParseAll[HNil] {
    type In = HNil
    def apply(value: HNil) = Some(HNil)
  }

  implicit def convertHList[T, HO <: HList](implicit 
    cv: Parse[T], 
    cl: ParseAll[HO]
  ) = new ParseAll[T :: HO] {
    type In = String :: cl.In
    def apply(value: In) = value match {
      case x :: xs => for {
        t <- cv(x)
        h0 <- cl(xs)
      } yield t :: h0
    }
  }
}

trait Converter {
  type Output
  def convert[S <: HList, H <: HList](values: List[String])(implicit
    gen: Generic.Aux[Output, H], // Compute HList representation `H` of Output
    parse: ParseAll.Aux[S, H],   // Generate parser of Hlist of String `S` to HList `H`
    ft: FromTraversable[S]       // Generate converter of `List[String]` to HList of Strings `S`
  ): Option[Output] =
    values.toHList[S].flatMap(parse.apply).map(gen.from)
}

升级此实现以返回您选择的错误monad(或抛出异常)而不是返回Option

It's simple to upgrade this implementation to return your error monad of choice (or to throw exceptions) instead of returning Option

这里是你如何使用它:

scala> object ConverterISD extends Converter {
  type Output = (Int, String, Double)
}

defined object ConverterISD

scala> ConverterISD.convert(List("1", "foo", "2.34"))
res0: Option[ConverterISD.Output] = Some((1,foo,2.34))

scala> ConverterISD.convert(List("1", "foo", "2.34", "5"))
res1: Option[ConverterISD.Output] = None

scala> ConverterISD.convert(List("1", "foo", "bar"))
res2: Option[ConverterISD.Output] = None

它也适用于案例类而不是元组:

It also works with case classes instead of tuples:

scala> case class Model(i: Int, d: Double)

defined class Model

scala> object ConverterModel extends Converter {
  type Output = Model
}

defined object ConverterModel

scala> ConverterModel.convert(List("1", "2.34"))
res0: Option[ConverterModel.Output] = Some(Model(1,2.34))

scala> ConverterModel.convert(List("1"))
res1: Option[ConverterModel.Output] = None

scala> ConverterModel.convert(List("1", "foo")) 
res2: Option[ConverterModel.Output] = None

这篇关于将Scala List[String]/List[Object] 转换成model/HList/tuple的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆