如何将Scala普通类序列化输出为CSV文件(不使用case类)
问题根因
你当前使用Jackson输出空字符串主要有两个原因:
- Scala 2.11 普通类的构造参数默认不会生成公开的访问方法,也不是公开字段,Jackson 默认无法读取到类的成员值
- 没有注册Jackson的Scala模块,无法正确识别Scala类的结构
方案1:修复Jackson配置实现序列化(推荐)
该方案可以自动处理CSV转义、分隔符兼容等场景,适合生产环境使用:
- 首先添加对应依赖(Scala 2.11 最高支持Jackson 2.12.x版本)
libraryDependencies ++= Seq( "com.fasterxml.jackson.dataformat" % "jackson-dataformat-csv" % "2.12.7", "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.12.7" )
- 类定义无需大改,仅需要给构造参数加
@BeanProperty注解即可:
import scala.beans.BeanProperty class FiveMinuteUser( @BeanProperty val field1: String, @BeanProperty val field2: String, // 剩余所有字段统一加@BeanProperty注解即可,无需其他修改 )
- 正确的序列化代码:
import com.fasterxml.jackson.dataformat.csv.CsvMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper // 初始化Mapper时必须注册Scala模块 val mapper = new CsvMapper with ScalaObjectMapper mapper.registerModule(DefaultScalaModule) // 生成CSV Schema val schema = mapper .schemaFor(classOf[FiveMinuteUser]) .withoutHeader .withColumnSeparator('\t') // 序列化单个实例 val user = new FiveMinuteUser("test1", "test2") val csvLine = mapper.writer(schema).writeValueAsString(user)
方案2:基于Scala反射实现通用序列化(无需修改原有类)
如果不希望修改原有类的定义,可以基于Scala反射写通用工具方法,不需要手动罗列任何字段:
import scala.reflect.runtime.universe._ object CsvConverter { def toCsv[T: TypeTag](instance: T, separator: Char = '\t'): String = { val mirror = runtimeMirror(instance.getClass.getClassLoader) val instanceMirror = mirror.reflect(instance) // 获取类所有val/var字段,保持类定义的顺序 val fields = typeOf[T].members .collect { case m: MethodSymbol if m.isVal || m.isVar => m } .toList .reverse // 读取字段值拼接为CSV行 fields .map(field => instanceMirror.reflectField(field).get.toString) .mkString(separator.toString) } } // 直接调用即可,不需要修改原有类的任何代码 val user = new FiveMinuteUser("test1", "test2") val csvLine = CsvConverter.toCsv(user)
注意事项
- 若字段内容包含分隔符、换行、引号等特殊字符,优先使用Jackson方案,会自动处理转义逻辑,反射方案需要自行补充转义代码
- 反射方案性能略低于Jackson,非超大规模数据量场景下无感知
- 若需要读取父类继承的字段,Jackson可新增配置
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)实现,反射方案可调整字段获取逻辑包含父类成员。
内容的提问来源于stack exchange,提问作者Vadim
相关产品推荐
相关产品推荐

