Как преобразовать строку в json в Spark 2 Scala
есть ли простой способ преобразования данного объекта строки в JSON?
нашел это о преобразовании всего фрейма данных в вывод json: Искра строка в 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()
}
работает только в том случае, если строка имеет только один уровень без вложенной строки. Это схема:
StructType(
StructField(indicator,StringType,true),
StructField(range,
StructType(
StructField(currency_code,StringType,true),
StructField(maxrate,LongType,true),
StructField(minrate,LongType,true)),true))
также попробовал предложение Артема, но это не скомпилировало:
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
}
7 ответов
можно использовать getValuesMap
чтобы преобразовать объект строки в карту, а затем преобразовать его 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 читает фрейм данных, он превращает запись в строку. Строка является структурой, подобной json. Это можно преобразовать и записать в json.
но мне нужно взять некоторые структуры sub json в строку для использования в качестве нового поля.
Это можно сделать так:
dataFrameWithJsonField = dataFrame.withColumn("address_json", to_json($"location.address"))
location.address
is путь к объекту sub json входящего фрейма данных на основе json. address_json
- имя столбца этого объекта, преобразованного в строковую версию json.
to_json
реализован в Spark 2.1.
если он генерирует выходной json, используя json4s address_json, должен быть проанализирован в представление AST, иначе выходной json будет иметь часть address_json.
по сути, вы можете иметь таблицу данных, который содержит только одну строку. Таким образом, вы можете попытаться отфильтровать исходный фрейм данных, а затем проанализировать его в json.
JSon имеет схему, но строка не имеет схемы, поэтому вам нужно применить схему к строке и преобразовать в 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
}
обратите внимание на класс scala scala.утиль.разбор.формат JSON.JSONObject устарел и не поддерживает значения null.
@deprecated ("этот класс будет удален.", "2.11.0")
" JSONFormat.defaultFormat не обрабатывает значения null"
я комбинирую предложение от: Артема, Киранма и Псидома. Сделал много трасс и ошибок и придумал эти решения, которые я тестировал для вложенных структур:
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
}
это решение работало, но только во время работы в режиме драйвера.
У меня была такая же проблема, у меня были паркетные файлы с канонической схемой (без массивов), и я хочу только получить события 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))