Java Spark UDF:如何在每行上编写一个UDF来提取嵌套结构中的特定值?
我正在使用Java中的Spark来处理XML文件。来自DataRicks的spark xml包用于将xml文件读入dataframe 示例xml文件包括:Java Spark UDF:如何在每行上编写一个UDF来提取嵌套结构中的特定值?,java,xml,apache-spark,apache-spark-sql,user-defined-functions,Java,Xml,Apache Spark,Apache Spark Sql,User Defined Functions,我正在使用Java中的Spark来处理XML文件。来自DataRicks的spark xml包用于将xml文件读入dataframe 示例xml文件包括: <RowTag> <id>1</id> <name>john</name> <expenses> <travel> <details> <date
<RowTag>
<id>1</id>
<name>john</name>
<expenses>
<travel>
<details>
<date>20191203</date>
<amount>400</amount>
</details>
</travel>
</expenses>
</RowTag>
df.printSchema()代码>显示如下:
root
|-- id: int(nullable = true)
|-- name: string(nullable = true)
|-- expenses: struct (nullable = true)
| |-- travel: struct (nullable = true)
| | |-- details: struct (nullable = true)
| | | |-- date: string (nullable = true)
| | | |-- amount: int (nullable = true)
| |-- food: struct (nullable = true)
| | |-- details: struct (nullable = true)
| | | |-- date: string (nullable = true)
| | | |-- amount: int (nullable = true)
所需的输出数据帧如下所示:
+--+------+-------------+
|id| name |expenses_date|
+---------+-------------+
|1 | john |20191203 |
|2 | joe |20191204 |
+--+------+-------------+
基本上,我想要一个通用的解决方案,用以下结构从xml中获取日期,其中只有标记
不同。
<RowTag>
<id>1</id>
<name>john</name>
<expenses>
**<X>**
<details>
<date>20191203</date>
<amount>400</amount>
</details>
**</X>**
</expenses>
</RowTag>
1.
厕所
****
20191203
400
****
我尝试过的:
spark.udf().register("getDate",(UDF1 <Row, String>) (Row row) -> {
return row.getStruct(0).getStruct(0).getAs("date").toString();
}, DataTypes.StringType);
df.select(callUDF("getDate",df.col("expenses")).as("expenses_date")).show();
spark.udf(){
返回row.getStruct(0).getStruct(0).getAs(“日期”).toString();
},DataTypes.StringType);
df.select(callUDF(“getDate”,df.col(“expenses”).as(“expenses_date”)).show();
但是它不起作用,因为row.getStruct(0)路由到
,但是对于row joe,在
下没有
标记,所以它返回了java.lang.NullPointerException
。我想要的是一个通用解决方案,它可以为每一行自动获取下一个标记名,例如,row.getStruct(0)
为row john路由到
,为row joe路由到
所以我的问题是:我应该如何重新制定我的UDF来实现这一目标
提前谢谢!!:) 该包允许您直接访问select表达式中的嵌套字段。你为什么要找UDF
df.selectExpr("id", "name", "COALESCE(`expenses`.`food`.`details`.`date`, `expenses`.`travel`.`details`.`date`) AS expenses_date" ).show()
输出:
+---+----+-------------+
| id|name|expenses_date|
+---+----+-------------+
| 1|john| 20191203|
| 2| joe| 20191204|
+---+----+-------------+
编辑
如果唯一正在更改的标记是expenses
struct之后的标记,则可以搜索expenses
下的所有字段,然后coalesce
列:expenses.X.details.date
。在Spark中类似这样的内容:
val expenses_fields = df.select(col("expenses.*")).columns
val date_cols = expenses_fields.map(f => col(s"`expenses`.`$f`.`details`.`date`"))
df.select(col("id"), col("name"), coalesce(date_cols: _*).alias("expenses_date")).show()
不过,您不需要使用UDF 如果你提到到目前为止你所做的一切都会有所帮助。这样您的代码/尝试可以得到改进或类似的东西。@VarunJain谢谢!我现在就做。嘿,谢谢你的回答。对不起,我没说清楚。因为我必须处理这样一种情况,即我不知道标签下会出现什么,它可能是或任何其他标签名称,在这种情况下,我需要一个UDF函数(或任何其他可能的方法)来概括这部分的解决方案。谢谢!这可以翻译成Java吗?我已经尝试过这个解决方案,.columns函数返回一个字符串[],我不能使用这个字符串来使用map函数……在Java8中,您也可以使用map。我没有这样做过:Arrays.stream(expenses\u fields).map(f->col(s)
expenses
$f详细信息
日期)).collect(Collectors.toList())
我也尝试过这个,但是collect函数将返回Java中的列表,而不是spark列……这实际上将返回List
。你所说的“用Java列表代替spark列”是什么意思?
+---+----+-------------+
| id|name|expenses_date|
+---+----+-------------+
| 1|john| 20191203|
| 2| joe| 20191204|
+---+----+-------------+
val expenses_fields = df.select(col("expenses.*")).columns
val date_cols = expenses_fields.map(f => col(s"`expenses`.`$f`.`details`.`date`"))
df.select(col("id"), col("name"), coalesce(date_cols: _*).alias("expenses_date")).show()