Spark使用反射動態的將文本數據映射到樣例類

Spark使用反射動態的將文本數據映射到樣例類

假如現在有一個tsv或者csv文件,文件中每條數據包含100+個字段.使用Spark讀取這個文件.我看有些人的做法是直接創建一個類,然後類的字段一個一個的傳.wdmy.要是有100多個字段,這不是很耗時?好吧,暫且不說耗時不好時,萬一一個不小心,寫錯了一個字段,那該怎麼辦?反正我比較喜歡偷懶,像這種的情況,一般使用偷奸耍滑的方法.

當然,使用反射的前提是:

  1. 不考慮反射時帶來的性能消耗.
  2. csv中字段數最好小於等於樣例類中的屬性個數.等於的不用管,小於的添加幾個空的佔位字段就好.
  3. 樣例類中字段的順序與csv中字段的順序一一對應.

ShowTime

package com.rm1024

import java.lang.reflect.Constructor

import org.apache.spark.sql.{DataFrame, SparkSession}

object ReflectReadCsv {
  // 反射獲取類的構造方法
  val cnstructor: Constructor[_] = classOf[Bean].getConstructors()(0)

  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .master("local[*]")
      .appName(this.getClass.getSimpleName)
      .getOrCreate()
    val csvPath = "path"
    readCSV(spark, csvPath).rdd.map(row => {
      // row中的字段,給他轉化成Seq
      var fields: Seq[String] = row.toSeq.map(_.asInstanceOf[String])
      // 假設樣例類的屬性與csv的字段一一對應
      // 笨方法
      Bean(fields(0), fields(1), fields(2), fields(3), fields(4), fields(5), fields(6), fields(7), fields(8), fields(9),
        fields(10), fields(11), fields(12), fields(13), fields(14), fields(15), fields(16), fields(17), fields(18), fields(19),
        fields(20), fields(21), fields(22), fields(23), fields(24), fields(25), fields(26), fields(27), fields(28), fields(29),
        fields(30), fields(31), fields(32), fields(33), fields(34),
      )

      // 反射創建
      cnstructor.newInstance(fields: _*)

      // 假設scv的字段沒有Bean的字段多,怎麼辦?補充幾個,使之相等.
      fields = fields ++ Seq("", "", "", "", "", "")
      // 然後再反射創建
      cnstructor.newInstance(fields: _*)
    })
    spark.stop()
  }


  /**
    * 讀取csv格式的數據
    *
    * @return
    * @param path   csv路徑
    * @param spark  SparkSession
    * @param header 有些csv是有頭文件的,有些沒有,看是否需要頭文件,true 表示需要頭文件,會將頭文件讀取進來,
    *               false表示不需要頭文件,則會吧第一行頭文件去掉.
    * @param sep    分隔符 不管是csv還是其他文件,字段之間的分隔符
    * @return
    */
  def readCSV(spark: SparkSession, path: String, header: Boolean = true, sep: String = "\t"): DataFrame = {
    spark.read.format("csv")
      .option("sep", sep)
      .option("inferSchema", "false") // 去掉類型推斷,都使用String類型
      .option("header", header)
      .load(path)
  }

}

case class Bean(
                 field0: String,
                 field1: String,
                 field2: String,
                 field3: String,
                 field4: String,
                 field5: String,
                 field6: String,
                 field7: String,
                 field8: String,
                 field9: String,
                 field10: String,
                 field11: String,
                 field12: String,
                 field13: String,
                 field14: String,
                 field15: String,
                 field16: String,
                 field17: String,
                 field18: String,
                 field19: String,
                 field20: String,
                 field21: String,
                 field22: String,
                 field23: String,
                 field24: String,
                 field25: String,
                 field26: String,
                 field27: String,
                 field28: String,
                 field29: String,
                 field30: String,
                 field31: String,
                 field32: String,
                 field33: String,
                 field34: String
               )

The End

可能有很多追求性能的大神們不喜歡我這種偷奸耍滑的程序員,但是我覺得代碼都好看多了,只是稍微付出那麼一點點性能,不好嗎?

免費贈送spark讀取文件時怎麼去掉第一行的解決方案.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章