我正在读一个包含二进制字符串字段的avro文件,我需要将其转换为java.lang.string以将其传递给另一个库(spark-xml-util),如何将其转换为java .lang.string有效。这是我到目前为止的代码: -
val df = sqlContext.read.format("com.databricks.spark.avro").load("filePath/fileName.avro")
df.select("myField").collect().mkString
最后一行给出了以下例外: -
Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.lang.String
at org.apache.spark.sql.Row$class.getString(Row.scala:255)
at org.apache.spark.sql.catalyst.expressions.GenericRow.getString(rows.scala:165)
df架构是: -
root
|-- id: string (nullable = true)
|-- myField: binary (nullable = true)
答案 0 :(得分:3)
现在考虑API的状态(2.2.0),最好的方法是创建一个UDF来做这个并替换列:
import org.apache.spark.sql.functions.udf
val toString = udf((payload: Array[Byte]) => new String(payload))
df.withColumn("myField", toString(df("myField")))
或者如果您似乎暗示数据是使用GZIP压缩的,您可以:
import org.apache.spark.sql.functions.udf
val toString = udf((payload: Array[Byte]) => {
val inputStream = new GZIPInputStream(new ByteArrayInputStream(payload))
scala.io.Source.fromInputStream(inputStream).mkString
})
df.withColumn("myField", toString(df("myField")))
答案 1 :(得分:0)
在先前的解决方案中,代码new String(payload)
对真正的二进制数据不起作用。
最终,解决方案要复杂得多,所需二进制数据的长度作为第二个参数。
def binToString(payload: Array[Byte], payload_length: Int): String = {
val ac: Array[Char] = Range(0,payload_length).map(i => payload(i).toChar).toArray
return ac.mkString
}
val binToStringUDF = udf( binToString(_: Array[Byte], _: Int): String )
答案 2 :(得分:0)
在Spark 3.0中,您可以在BINARY和STRING数据之间进行转换。
scala> val df = sc.parallelize(Seq("ABC", "BCD", "CDE", "DEF")).toDF("value")
df: org.apache.spark.sql.DataFrame = [value: string]
scala> df.select($"value", $"value".cast("BINARY"),
$"value".cast("BINARY").cast("STRING")).show()
+-----+----------+-----+
|value| value|value|
+-----+----------+-----+
| ABC|[41 42 43]| ABC|
| BCD|[42 43 44]| BCD|
| CDE|[43 44 45]| CDE|
| DEF|[44 45 46]| DEF|
+-----+----------+-----+
我没有要测试的数据,但是您应该可以:
df.select($"myField".cast("STRING"))
这显然取决于实际数据(即,不要将JPEG转换为STRING),但假设它是UTF-8编码的,就可以了。