Json 从scala中的CSV列中发现类型

Json 从scala中的CSV列中发现类型,json,scala,csv,jackson,play-json,Json,Scala,Csv,Jackson,Play Json,我想将标题为但列号未知的通用CSV文件读入类型化结构。我的问题与之类似,但事实上我没有模式可以传递给解析器 到目前为止,我一直在使用Jackson CSV mapper将每一行作为一个映射来读取[String,String],它工作得很好 import com.fasterxml.jackson.module.scala.DefaultScalaModule def genericStringIterator(输入:InputStream):迭代器[Map[String,String]]={ v

我想将标题为但列号未知的通用CSV文件读入类型化结构。我的问题与之类似,但事实上我没有模式可以传递给解析器

到目前为止,我一直在使用Jackson CSV mapper将每一行作为一个映射来读取[String,String],它工作得很好

import com.fasterxml.jackson.module.scala.DefaultScalaModule
def genericStringIterator(输入:InputStream):迭代器[Map[String,String]]={
val mapper=new CsvMapper()
mapper.registerModule(DefaultScalaModule)
val schema=CsvSchema.emptySchema.withHeader
val迭代器=映射器
.reader(classOf[Map[String,String]])
.`with`(模式)
.readValues[Map[String,String]](输入)
迭代器
}
现在,我们需要键入字段,因此4.2将是一个双精度字段,但“4.2”仍然是一个字符串

在我们的项目中,我们到处都在使用play json,所以我知道JsValue已经有了一个很好的泛型推断

因为paly json也是基于Jackson的,所以我觉得有这样的东西会很棒

import play.api.libs.json.jackson.PlayJsonModule
def genericStringIterator(输入:InputStream):迭代器[JsValue]={
val mapper=new CsvMapper()
mapper.registerModule(PlayJsonModule)
val schema=CsvSchema.emptySchema.withHeader
val迭代器=映射器
.readerFor(classOf[JsValue])
.`with`(模式)
.readValues[JsValue](输入)
迭代器
}
但当我尝试前一个代码时,我得到了一个例外:

   val iterator = CSV.genericAnyIterator(input(
      """foo,bar,baz
        |"toto",42,43
        |"tata",,45
        | titi,87,88
        |"tutu",,
        |""".stripMargin))

    iterator
      .foreach { a =>
        println(a)
      }
java.lang.RuntimeException:我们应该在读地图,出了点问题
atplay.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:165)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:128)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:123)
位于com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:277)
位于com.fasterxml.jackson.databind.MappingIterator.next(MappingIterator.java:192)
位于scala.collection.convert.Wrappers$JIteratorWrapper.next(Wrappers.scala:40)
位于scala.collection.Iterator.foreach(Iterator.scala:929)
位于scala.collection.Iterator.foreach$(Iterator.scala:929)
位于scala.collection.AbstractIterator.foreach(迭代器.scala:1417)
在my.company.csv.CSVSpec$$anon$4。(CSVSpec.scala:240)
我做错什么了吗


我不在乎最后是否有一个play json JsValue,任何带有泛型类型字段的json结构都可以。有没有其他的库我可以用呢?就我所发现的,所有其他lib都基于预先提供给CSV阅读器的映射,对我来说重要的是能够从CSV推断出类型

好吧,我懒得想找到现成的东西:) 事实上,这是很容易实现自己

我研究了其他语言中进行这种推断的lib(JS中的PapaParse,python中的Pandas),发现他们正在做一个测试,然后优先重试以猜测类型

所以我自己实现了它,它工作得很好

这是:

def genericAnyIterator(输入:InputStream):迭代器[JsValue]={
//前一个代码的结果,映射到Map[String,String]
val strings=genericStringIterator(输入)
val decimalRegExp:Regex=“(\\d*){1}(\\.\\d*){0,1}”。r
val jsValues:Iterator[Map[String,JsValue]=strings.Map{m=>
m、 映射值{
案例“”=>无
case“false”|“false”=>Some(JsFalse)
大小写“true”|“true”=>Some(JsTrue)
案例value@decimalRegExp(g1,g2)if!value.isEmpty=>Some(JsNumber(value.toDouble))
大小写“null”|“null”=>Some(JsNull)
案例value@_=>一些(JsString(值))
}
.filter(u._2.isDefined)
.mapValues(u.get)
}
jsValues.map(map=>JsObject(map.toSeq))
}
这在考试中起什么作用

 it should "read any csv in JsObject" in new WithInputStream {

    val iterator = CSV.genericAnyIterator(input(
      """foo,bar,baz
        |"toto",NaN,false
        |"tata",,TRUE
        |titi,87.79,88
        |"tutu",,null
        |"tete",5.,.5
        |""".stripMargin))

    val result: Seq[JsValue] = iterator.toSeq

    result should be(Stream(
      Json.obj("foo" -> "toto", "bar" -> "NaN", "baz" -> false)
      , Json.obj("foo" -> "tata",  "baz" -> true)
      , Json.obj("foo" -> "titi", "bar" -> 87.79, "baz" -> 88)
      , Json.obj("foo" -> "tutu",  "baz" -> JsNull)
      , Json.obj("foo" -> "tete", "bar" -> 5, "baz" -> 0.5)
    ) )
  }

对于这种情况来说,这是一种过激的手段,但你可以用Shapeble来解决这个问题。@sinanspd我不确定Shapeble在这方面是否有帮助。基于未成形,但仍需要声明将读取的类型。
 it should "read any csv in JsObject" in new WithInputStream {

    val iterator = CSV.genericAnyIterator(input(
      """foo,bar,baz
        |"toto",NaN,false
        |"tata",,TRUE
        |titi,87.79,88
        |"tutu",,null
        |"tete",5.,.5
        |""".stripMargin))

    val result: Seq[JsValue] = iterator.toSeq

    result should be(Stream(
      Json.obj("foo" -> "toto", "bar" -> "NaN", "baz" -> false)
      , Json.obj("foo" -> "tata",  "baz" -> true)
      , Json.obj("foo" -> "titi", "bar" -> 87.79, "baz" -> 88)
      , Json.obj("foo" -> "tutu",  "baz" -> JsNull)
      , Json.obj("foo" -> "tete", "bar" -> 5, "baz" -> 0.5)
    ) )
  }