我正在尝试使用Spark Structured streaming从Kafka主题中读取XML数据。
我尝试使用Databricks spark-xml
包,但是我收到一条错误消息,说明此包不支持流式读取。有什么办法可以使用结构化流媒体从Kafka主题中提取XML数据吗?
我目前的代码:
df = spark \
.readStream \
.format("kafka") \
.format('com.databricks.spark.xml') \
.options(rowTag="MainElement")\
.option("kafka.bootstrap.servers", "localhost:9092") \
.option(subscribeType, "test") \
.load()
错误:
py4j.protocol.Py4JJavaError: An error occurred while calling o33.load.
: java.lang.UnsupportedOperationException: Data source com.databricks.spark.xml does not support streamed reading
at org.apache.spark.sql.execution.datasources.DataSource.sourceSchema(DataSource.scala:234)
答案 0 :(得分:5)
.format("kafka") \ .format('com.databricks.spark.xml') \
com.databricks.spark.xml
的最后一个获胜并成为流媒体源(隐藏Kafka作为源)。
换言之,上述内容仅相当于.format('com.databricks.spark.xml')
。
正如您可能经历的那样,Databricks spark-xml
包不支持流式读取(即不能充当流式源)。该软件包不适用于流式传输。
有什么办法可以使用结构化流媒体从Kafka主题中提取XML数据吗?
您可以使用标准函数或UDF自行访问和处理XML。结构化流中直到Spark 2.2.0的流式XML处理没有内置支持。
无论如何,这应该不是什么大问题。 Scala代码可能如下所示。
val input = spark.
readStream.
format("kafka").
...
load
val values = input.select('value cast "string")
val extractValuesFromXML = udf { (xml: String) => ??? }
val numbersFromXML = values.withColumn("number", extractValuesFromXML('value))
// print XMLs and numbers to the stdout
val q = numbersFromXML.
writeStream.
format("console").
start
另一种可能的解决方案是编写自己的自定义流Source,以处理def getBatch(start: Option[Offset], end: Offset): DataFrame
中的XML格式。那个 应该有效。
答案 1 :(得分:2)
import xml.etree.ElementTree as ET
df = spark \
.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option(subscribeType, "test") \
.load()
然后我写了一个python UDF
def parse(s):
xml = ET.fromstring(s)
ns = {'real_person': 'http://people.example.com',
'role': 'http://characters.example.com'}
actor_el = xml.find("DNmS:actor",ns)
if(actor_el ):
actor = actor_el.text
role_el.find('real_person:role', ns)
if(role_el):
role = role_el.text
return actor+"|"+role
注册此UDF
extractValuesFromXML = udf(parse)
XML_DF= df .withColumn("mergedCol",extractroot("value"))
AllCol_DF= xml_DF.withColumn("actorName", split(col("mergedCol"), "\\|").getItem(0))\
.withColumn("Role", split(col("mergedCol"), "\\|").getItem(1))
答案 2 :(得分:1)
你不能以这种方式混合格式。 Kafka源加载为Row
,包括key
,value
和topic
等值的数量,其中value
列存储payload as a binary
type:
请注意,无法设置以下Kafka参数,Kafka源或接收器将抛出异常:
...
value.deserializer :值始终使用ByteArrayDeserializer反序列化为字节数组。使用DataFrame操作显式反序列化值。
解析此内容是用户的责任,不能委托给其他数据源。例如,请参阅我对How to read records in JSON format from Kafka using Structured Streaming?的回答。
对于XML,您可能需要一个UDF(UserDefinedFunction
),尽管您可以先尝试Hive XPath functions。您还应解码二进制数据。
答案 3 :(得分:0)
使用现有库,
https://github.com/databricks/spark-xml
&foreachBatch
(Spark 2.4 +)
inputStream.writeStream.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
var parameters = collection.mutable.Map.empty[String, String]
var schema: StructType = null
val rdd:RDD[String] = batchDF.as[String].rdd
val relation = XmlRelation(
() => rdd,
None,
parameters.toMap,
schema)(spark.sqlContext)
spark.baseRelationToDataFrame(relation)
.write.format("parquet")
.mode("append")
.saveAsTable("default.catalog_sink")
}.start()
spark.baseRelationToDataFrame(relation)
将返回在批处理模式下执行spark-xml的所有操作,您可以在该数据帧上使用sparksql来得出所需的确切结果。
答案 4 :(得分:0)
看起来上面的方法可行,但是它没有使用传递的模式来解析XML文档。
如果打印关系模式,则始终为
INFO XmlToAvroConverter - .convert() : XmlRelation Schema ={} root
|-- fields: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- name: string (nullable = true)
| | |-- nullable: boolean (nullable = true)
| | |-- type: string (nullable = true)
|-- type: string (nullable = true)
例如:我正在按照Kafka主题流式传输以下XML文档
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Book>
<Author>John Doe</Author>
<Title>Test</Title>
<PubishedDate></PublishedDate>
</Book>
这是我必须将XML解析为DataFrame的代码
kafkaValueAsStringDF = kafakDF.selectExpr("CAST(key AS STRING) msgKey","CAST(value AS STRING) xmlString")
var parameters = collection.mutable.Map.empty[String, String]
parameters.put("rowTag", "Book")
kafkaValueAsStringDF.writeStream.foreachBatch {
(batchDF: DataFrame, batchId: Long) =>
val xmlStringDF:DataFrame = batchDF.selectExpr("xmlString")
xmlStringDF.printSchema()
val rdd: RDD[String] = xmlStringDF.as[String].rdd
val relation = XmlRelation(
() => rdd,
None,
parameters.toMap,
xmlSchema)(spark.sqlContext)
logger.info(".convert() : XmlRelation Schema ={} "+relation.schema.treeString)
}
.start()
.awaitTermination()
当我从文件系统或S3中读取相同的XML文档并使用spark-xml时,它将按预期方式解析架构。
谢谢 卫星
答案 5 :(得分:0)
您可以使用 SQL built-in 函数 xpath
等从作为 Kafka 消息的值出现的嵌套 XML 结构中提取数据。
给定一个像嵌套的 XML
<root>
<ExecutionTime>20201103153839</ExecutionTime>
<FilterClass>S</FilterClass>
<InputData>
<Finance>
<HeaderSegment>
<Version>6</Version>
<SequenceNb>1</SequenceNb>
</HeaderSegment>
</Finance>
</InputData>
</root>
然后您就可以在 selectExpr
语句中使用这些 SQL 函数,如下所示:
df.readStream.format("kafka").options(...).load()
.selectExpr("CAST(value AS STRING) as value")
.selectExpr(
"xpath(value, '/CofiResults/ExecutionTime/text()') as ExecutionTimeAsArryString",
"xpath_long(value, '/CofiResults/ExecutionTime/text()') as ExecutionTimeAsLong",
"xpath_string(value, '/CofiResults/ExecutionTime/text()') as ExecutionTimeAsString",
"xpath_int(value, '/CofiResults/InputData/Finance/HeaderSegment/Version/text()') as VersionAsInt")
请记住,xpath
函数将返回一个 Array 字符串,而您可能会发现将值提取为 String 甚至 Long 会更方便。在带有控制台接收器流的 Spark 3.0.1 中应用上面的代码将导致:
+-------------------------+-------------------+---------------------+------------+
|ExecutionTimeAsArryString|ExecutionTimeAsLong|ExecutionTimeAsString|VersionAsInt|
+-------------------------+-------------------+---------------------+------------+
|[20201103153839] |20201103153839 |20201103153839 |6 |
+-------------------------+-------------------+---------------------+------------+