【问题标题】:Convert scala List[String]/List[Object] into model/HList/tuple将 scala List[String]/List[Object] 转换为 model/HList/tuple
【发布时间】:2016-01-25 23:46:25
【问题描述】:

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

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

显然,我不想每次都实现转换方法。

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

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

【问题讨论】:

    标签: scala shapeless hlist


    【解决方案1】:

    首先,convert 方法的输出必须是Option[Output],或者Output 的一些单子(TryEitherscalaz.\/scalaz.Validation 等)如果Seq[String]的内容不能转换为OutputSeq的长度错误,Ints或Doubles的解析错误等)

    使用shapeless 的可能实现将有一个类型类将String 转换为其参数类型,以及一个辅助类型类将StringHList 转换为OutputHList 表示,使用第一个类型类。

    这是一个示例实现:

    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)
    }
    

    升级此实现以返回您选择的错误单子(或抛出异常)而不是返回 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
    

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

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-27
      • 2020-01-11
      • 2018-12-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多