簡體   English   中英

如何在 Spark 2 Scala 中將 Row 轉換為 json

[英]How to convert Row to json in Spark 2 Scala

有沒有一種簡單的方法可以將給定的 Row 對象轉換為 json?

發現這個關於將整個數據幀轉換為 json 輸出: Spark Row to JSON

但我只想將一行轉換為 json。 這是我正在嘗試做的偽代碼。

更准確地說,我正在讀取 json 作為數據幀中的輸入。 我正在生成一個主要基於列的新輸出,但有一個 json 字段用於所有不適合列的信息。

我的問題是編寫此函數的最簡單方法是什么:convertRowToJson()

def convertRowToJson(row: Row): String = ???

def transformVenueTry(row: Row): Try[Venue] = {
  Try({
    val name = row.getString(row.fieldIndex("name"))
    val metadataRow = row.getStruct(row.fieldIndex("meta"))
    val score: Double = calcScore(row)
    val combinedRow: Row = metadataRow ++ ("score" -> score)
    val jsonString: String = convertRowToJson(combinedRow)
    Venue(name = name, json = jsonString)
  })
}

Psidom 的解決方案:

def convertRowToJSON(row: Row): String = {
    val m = row.getValuesMap(row.schema.fieldNames)
    JSONObject(m).toString()
}

僅當 Row 只有一層而不是嵌套 Row 時才有效。 這是架構:

StructType(
    StructField(indicator,StringType,true),   
    StructField(range,
    StructType(
        StructField(currency_code,StringType,true),
        StructField(maxrate,LongType,true), 
        StructField(minrate,LongType,true)),true))

還嘗試了 Artem 建議,但沒有編譯:

def row2DataFrame(row: Row, sqlContext: SQLContext): DataFrame = {
  val sparkContext = sqlContext.sparkContext
  import sparkContext._
  import sqlContext.implicits._
  import sqlContext._
  val rowRDD: RDD[Row] = sqlContext.sparkContext.makeRDD(row :: Nil)
  val dataFrame = rowRDD.toDF() //XXX does not compile
  dataFrame
}

您可以使用getValuesMap將行對象轉換為 Map,然后將其轉換為 JSON:

import scala.util.parsing.json.JSONObject
import org.apache.spark.sql._

val df = Seq((1,2,3),(2,3,4)).toDF("A", "B", "C")    
val row = df.first()          // this is an example row object

def convertRowToJSON(row: Row): String = {
    val m = row.getValuesMap(row.schema.fieldNames)
    JSONObject(m).toString()
}

convertRowToJSON(row)
// res46: String = {"A" : 1, "B" : 2, "C" : 3}

我需要讀取 json 輸入並生成 json 輸出。 大多數字段都是單獨處理的,但只需要保留一些 json 子對象。

當 Spark 讀取數據幀時,它將記錄轉換為行。 Row 是一個類似 json 的結構。 可以將其轉換並寫出到 json。

但是我需要將一些子 json 結構取出到字符串中以用作新字段。

這可以像這樣完成:

dataFrameWithJsonField = dataFrame.withColumn("address_json", to_json($"location.address"))

location.address是傳入的基於 json 的數據幀的子 json 對象的路徑。 address_json是該對象的列名稱轉換為 json 的字符串版本。

to_json在 Spark 2.1 中實現。

如果使用 json4s address_json 生成它輸出 json 應該被解析為 AST 表示,否則輸出 json 將有 address_json 部分轉義。

注意 scala 類 scala.util.parsing.json.JSONObject 已棄用且不支持空值。

@deprecated("這個類將被刪除。", "2.11.0")

“JSONFormat.defaultFormat 不處理空值”

https://issues.scala-lang.org/browse/SI-5092

本質上,您可以擁有一個僅包含一行的數據框。 因此,您可以嘗試過濾初始數據幀,然后將其解析為 json。

JSon 有架構,但 Row 沒有架構,因此您需要在 Row 上應用架構並轉換為 JSon。 這是您如何做到的。

import org.apache.spark.sql.Row
import org.apache.spark.sql.types._

def convertRowToJson(row: Row): String = {

  val schema = StructType(
      StructField("name", StringType, true) ::
      StructField("meta", StringType, false) ::  Nil)

      return sqlContext.applySchema(row, schema).toJSON
}

我有同樣的問題,我有規范模式(沒有數組)的鑲木地板文件,我只想獲取 json 事件。 我做了如下,它似乎工作得很好(Spark 2.1):

import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.{DataFrame, Dataset, Row}
import scala.util.parsing.json.JSONFormat.ValueFormatter
import scala.util.parsing.json.{JSONArray, JSONFormat, JSONObject}

def getValuesMap[T](row: Row, schema: StructType): Map[String,Any] = {
  schema.fields.map {
    field =>
      try{
        if (field.dataType.typeName.equals("struct")){
          field.name -> getValuesMap(row.getAs[Row](field.name),   field.dataType.asInstanceOf[StructType]) 
        }else{
          field.name -> row.getAs[T](field.name)
        }
      }catch {case e : Exception =>{field.name -> null.asInstanceOf[T]}}
  }.filter(xy => xy._2 != null).toMap
}

def convertRowToJSON(row: Row, schema: StructType): JSONObject = {
  val m: Map[String, Any] = getValuesMap(row, schema)
  JSONObject(m)
}
//I guess since I am using Any and not nothing the regular ValueFormatter is not working, and I had to add case jmap : Map[String,Any] => JSONObject(jmap).toString(defaultFormatter)
val defaultFormatter : ValueFormatter = (x : Any) => x match {
  case s : String => "\"" + JSONFormat.quoteString(s) + "\""
  case jo : JSONObject => jo.toString(defaultFormatter)
  case jmap : Map[String,Any] => JSONObject(jmap).toString(defaultFormatter)
  case ja : JSONArray => ja.toString(defaultFormatter)
  case other => other.toString
}

val someFile = "s3a://bucket/file"
val df: DataFrame = sqlContext.read.load(someFile)
val schema: StructType = df.schema
val jsons: Dataset[JSONObject] = df.map(row => convertRowToJSON(row, schema))

如果您正在遍歷數據幀,則可以直接將數據幀轉換為內部包含 json 對象的新數據幀並對其進行迭代

val df_json = df.toJSON

我結合了來自 Artem、KiranM 和 Psidom 的建議。 做了很多跟蹤和錯誤,並提出了我測試嵌套結構的解決方案:

def row2Json(row: Row, sqlContext: SQLContext): String = {
  import sqlContext.implicits
  val rowRDD: RDD[Row] = sqlContext.sparkContext.makeRDD(row :: Nil)
  val dataframe = sqlContext.createDataFrame(rowRDD, row.schema)
  dataframe.toJSON.first
}

此解決方案有效,但僅在以驅動程序模式運行時有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM