以类型安全的方式将Seq [String]转换为case类

时间:2014-05-08 11:30:55

标签: scala scalaz shapeless

我编写了一个解析器,它按照一些规则将String转换为Seq [String]。这将在库中使用。

我正在尝试将此Seq [String]转换为case类。案例类将由用户提供(因此无法猜测它将是什么)。

我曾想过无形的库,因为它似乎实现了很好的功能,看起来很成熟,但我不知道如何继续。

我发现了这个问题with an interesting answer,但我找不到如何根据我的需要改造它。实际上,在答案中只有一种类型要解析(String),并且库在String本身内部迭代。它可能需要对事情的完成方式进行深刻的改变,而且我也不知道如何。

此外,如果可能的话,我想让我的图书馆用户尽可能轻松地完成这个过程。因此,如果可能的话,与上面链接中的答案不同,HList类型将从案例类本身猜测(但根据我的搜索,似乎编译器需要此信息)。

我对类型系统和所有这些美好的东西都有点新鲜,如果有人能给我一个如何做的建议,我会非常高兴!

亲切的问候

---编辑---

正如ziggystar所要求的,这里有一些可能需要的签名:

//Let's say we are just parsing a CSV.

@onUserSide
case class UserClass(i:Int, j:Int, s:String)
val list = Seq("1,2,toto", "3,4,titi")

// User transforms his case class to a function with something like:
val f = UserClass.curried

// The function created in 1/ is injected in the parser
val parser = new Parser(f)

// The Strings to convert to case classes are provided as an argument to the parse() method.
val finalResult:Seq[UserClass] = parser.parse(list) 
// The transfomation is done in two steps inside the parse() method:
// 1/ first we have: val list = Seq("1,2,toto", "3,4,titi")
// 2/ then we have a call to internalParserImplementedSomewhereElse(list)
//    val parseResult is now equal to Seq(Seq("1", "2", "toto"), Seq("3","4", "titi"))
// 3/ finally Shapeless do its magick trick and we have Seq(UserClass(1,2,"toto"), UserClass(3,4,"titi))



@insideTheLibrary
class Parser[A](function:A) {

 //The internal parser takes each String provided through argument of the method and transforms each String to a Seq[String]. So the Seq[String] provided is changed to Seq[Seq[String]]. 
 private def internalParserImplementedSomewhereElse(l:Seq[String]): Seq[Seq[String]] = {
  ...
 }

 /*
 * Class A and B are both related to the case class provided by the user: 
 * - A is the type of the case class as a function, 
 * - B is the type of the original case class (can be guessed from type A).
 */
 private def convert2CaseClass[B](list:Seq[String]): B {
    //do  something with Shapeless
    //I don't know what to put inside ???
 }

 def parse(l:Seq[String]){
   val parseResult:Seq[Seq[String]] = internalParserImplementedSomewhereElse(l:Seq[String])
   val finalResult = result.map(convert2CaseClass)
   finalResult // it is a Seq[CaseClassProvidedByUser]       
 }
}    

在库内部有一些隐式的可用于将String转换为正确的类型,因为它们是由Shapeless猜测的(类似于上面链接中提出的答案)。像string.toInt,string.ToDouble等......

可能有其他方式来设计它。这是我在玩Shapeless几个小时后的想法。

2 个答案:

答案 0 :(得分:1)

这使用了一个名为product-collecions

的非常简单的库
import com.github.marklister.collections.io._
case class UserClass(i:Int, j:Int, s:String)

val csv = Seq("1,2,toto", "3,4,titi").mkString("\n")
csv: String =
1,2,toto
3,4,titi

CsvParser(UserClass).parse(new java.io.StringReader(csv))
res28: Seq[UserClass] = List(UserClass(1,2,toto), UserClass(3,4,titi))

并以另一种方式序列化:

scala> res28.csvIterator.toList
res30: List[String] = List(1,2,"toto", 3,4,"titi")

product-collections面向csv和java.io.Reader,因此是上面的垫片。

答案 1 :(得分:0)

这个答案不会告诉你如何做你想要的,但它会解决你的问题。我认为你过度复杂了。

你想做什么?在我看来,您只是在寻找 serialize 反序列化案例类的方法 - 即将您的Scala对象转换为通用字符串格式和返回Scala对象的通用字符串格式。您的序列化步骤目前是您已经定义的,并且您正在询问如何进行反序列化。

Scala有一些序列化/反序列化选项。你不必用Shapeless或Scalaz来自己做。试着看看这些解决方案:

  1. Java serialization/deserialization。 Java环境提供的常规序列化/反序列化工具。需要显式转换,无法控制序列化格式,但它内置并且不需要太多工作来实现。
  2. JSON序列化:有许多库为Java提供JSON生成和解析。例如,请查看play-jsonspray-jsonArgonaut
  3. Scala Pickling库是一个更通用的序列化/反序列化库。开箱即用它带有一些二进制和一些JSON格式,但您可以创建自己的格式。
  4. 在这些解决方案中,至少play-json和Scala Pickling在编译时使用宏来生成序列化器和反序列化器。这意味着它们应该是类型安全和高效的。