读取记录的嵌套列表时发生ClassCastException-Dataflow&BigQuery

时间:2018-08-29 18:39:44

标签: java google-bigquery google-cloud-dataflow

从Dataflow中读取BigQuery表,其中的一个字段是“记录”和“重复”字段。所以我期望Java中的结果数据类型为List

但是,当我尝试遍历列表时,出现以下异常: java.lang.ClassCastException:java.util.LinkedHashMap无法转换为com.google.api.services.bigquery.model.TableRow < / em>

表架构看起来像这样:

{
    "id": "my_id",
    "values": [
        {
            "nested_record": "nested"
        }
    ]
}

迭代值的代码如下所示:

String id = (String) row.get("id");
List<TableRow> values = (List<TableRow>) row.get("values");

for (TableRow nested : values) {
    // more  logic
}

在循环开始的地方抛出异常。 这里明显的解决方法是将值强制转换为LinkedHashMaps的列表,但这感觉不对。 有谁知道为什么数据流会为嵌套的“记录”引发此类错误?

  • 非常感谢您的帮助!

2 个答案:

答案 0 :(得分:0)

当我尝试使用Google Cloud DataFlow从BigQuery读取嵌套表时,遇到了同样的ClassCastException。最后通过将TableRow强制转换为不同的数据结构来解决,具体取决于我使用的是哪个DataFlow运行器:

  • 如果使用DirectRunner:投射到LinkedHashMap
  • 如果使用DataflowRunner:强制转换为TableRow

示例:

Object valuesList = row.get("values");
// DirectRunner
for (TableRow v : (List<LinkedHashMap>) valuesList) {
   String name = v.get("name");
   String age = v.get("age");
}

// DataflowRunner
for (TableRow v : (List<TableRow>) valuesList) {
   String name = v.get("name");
   String age = v.get("age");
}

答案 1 :(得分:0)

看看BEAM-2767

造成这种情况的根本原因是DirectRunner在步骤之间执行编码往返,这通常不在Dataflow中执行。作为Table字段访问重复记录(或任何记录)将在这两个运行程序上成功执行,因为TableRow实现了Map接口。 记录被读取为“ TableRow”类型,但是在对它们进行编码时,它们被编码为简单的JSON映射。因为JSON编码器无法识别地图的字段类型,所以它将记录反序列化为简单的地图类型。

TableRow是一个Map,因此您可以将两种情况都视为Map:

    String id = (String) row.get("id");
    List<? extends Map> values = row.get("values");

    for (Map nested : values) {
        // more  logic
    }