java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap无法强制转换为java.util.LinkedHashMap

时间:2017-12-29 19:42:16

标签: java google-cloud-dataflow apache-beam dataflow

我为打开关于这个一般性问题的另一个问题而道歉,但我在SO上发现的问题似乎都与我的问题密切相关。

我有一个现有的工作数据流管道,它接受KV<Long, Iterable<TableRow>>对象并输出TableRow个对象。此代码在我们的生产环境中运行,没有问题。我现在正试图用直接转轮实现单元测试来测试这个管道,但单元测试在它到达线时失败

LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);

在管道中,抛出错误消息:

  

java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap   无法转换为java.util.LinkedHashMap

现有数据流代码的简化版本如下所示:

public static class Process extends DoFn<KV<Long, Iterable<TableRow>>, TableRow> {

    /* private variables */

    /* constructor */

    /* private functions */

    @ProcessElement
    public void processElement(ProcessContext c) throws InterruptedException, ParseException {       

      EventProcessor eventProc = new EventProcessor();
      Processor.WorkItem workItem = new Processor.WorkItem();
      Iterator<TableRow> it = c.element().getValue().iterator();

      // process all TableRows having the same id
      while (it.hasNext()) {
        TableRow item = it.next();

        if (item.containsKey(Schema.EVT))
          eventProc.process(item, workItem);
        else
          /* process by different Proc class */
      }

      /* do additional logic */

      /* c.output() is somewhere far below */

    }
}

public class EventProcessor extends Processor {

    // Extract data from an event into the WorkItem
    @SuppressWarnings("unchecked")
    @Override
    public void process(TableRow row, WorkItem item) {    

        try {
          LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);
          LinkedHashMap<String, Object> profile = (LinkedHashMap<String, Object>) row.get(Schema.PROFILE);

          /* if no exception, process further business logic */

          /* business logic */

        } catch (ParseException e) {
            System.err.println("Bad row");
        }
    }
}      

单元测试的相关部分(准备Process() DoFn的主要输入)如下所示:

Map<Long, List<TableRow>> groups = new HashMap<Long, List<TableRow>>();
List<KV<Long, Iterable<TableRow>>> collections = new ArrayList<KV<Long,Iterable<TableRow>>>();    
Gson gson = new Gson();    

// populate the map with events grouped by id
for(int i = 0; i < EVENTS.length; i++) {
  TableRow row = gson.fromJson(EVENTS[i], TableRow.class);
  Long id = EVENT_IDS[i];

  if(groups.containsKey(id))
    groups.get(id).add(row);
  else
    groups.put(id, new ArrayList<TableRow>(Arrays.asList(row)));        
}

// prepare main input for pipeline
for(Long key : groups.keySet())
  collections.add(KV.of(key, groups.get(key)));

导致问题的行是gson.fromJson(EVENTS[i], TableRow.class);,它似乎将TableRow的内部表示编码为LinkedTreeMap的错误类型。

TableRow的编码类型似乎是com.google.gson.internal.LinkedTreeMap而不是预期的java.util.LinkedHashMap。有没有办法可以将我在单元测试中创建的TableRow转换为正确的java.util.LinkedHashMap类型,以便单元测试成功,而不对已在生产中工作的现有数据流代码进行任何更改?

3 个答案:

答案 0 :(得分:2)

将解决方案作为答案重新发布。

如果您不使用其特定功能,则不建议转换为具体类。在这种情况下,最好转换为Map而不是LinkedHashMap。 Gson的LinkedTreeMap也是Map,因此不会出现任何问题。

答案 1 :(得分:0)

这是因为LinkedHashMap并不优于LinkedTreeMap,因此它们可能没有相同的方法。因此,Java编译器认为以这种方式进行转换可能会导致evt使用与row.get(Schema.EVT)不同的方法,从而导致错误的东西。 但是,您可以将LinkedTreeMap转换为AbstractMap,Map或Object,因为它们都优于它。 所以(正如许多评论指出的那样)修复它,只需使用

Map<String, Object> evt = row.get(Schema.EVT);

你应该没事。

答案 2 :(得分:0)

我会考虑(不仅如此)投下code smell。每次对演员表进行编码时,都会冒ClassCastException发生的风险。

正如其他人已经说过的那样,Map界面可以像Map<String, Object> evt = row.get(Schema.EVT);一样使用。

或者,LinkedHashMap可以构建新的new LinkedHashMap<String, Object>(row.get(Schema.EVT));

第二种方法的优点是保持LinkedHashMap类型,这可能会也可能不重要,这取决于您的情况。