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

Posted

技术标签:

【中文标题】java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap【英文标题】:java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to java.util.LinkedHashMap 【发布时间】:2018-06-10 04:35:57 【问题描述】:

对于就这个一般性问题提出另一个问题,我深表歉意,但我在 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 类型,以便单元测试成功,而无需对已经在生产中运行的现有数据流代码进行任何更改?

【问题讨论】:

如果需要更多代码来添加上下文,请告诉我。 为什么需要转换为LinkedHashMap 而不仅仅是Map @RomanPuchkovskiy 老实说,我不确定。 LinkedHashMap 的演员表是以前某个时候的现有代码。将演员表更改为Map 会有什么后果吗? 没错。针对接口编写代码并将LinkedHashMap 替换为Map。那么你就不需要关心 API 碰巧返回了哪个地图实现。 @Max 如果LinkedTreeMap Map 的孩子,那应该不是问题,除非您使用该类的特定内容。但我认为你最好的选择是尝试看看它是否有效 【参考方案1】:

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

如果您不使用具体类的特定功能,则不建议转换为具体类。在这种情况下,最好转换为Map 而不是LinkedHashMap。 Gson 的LinkedTreeMap 也是Map,所以应该没有问题。

【讨论】:

【参考方案2】:

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

正如其他人已经说过的,Map 接口可以像Map&lt;String, Object&gt; evt = row.get(Schema.EVT); 一样使用。

或者,new LinkedHashMap&lt;String, Object&gt;(row.get(Schema.EVT)); 可以构造一个新的LinkedHashMap

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

【讨论】:

【参考方案3】:

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

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

你应该没事的。

【讨论】:

以上是关于java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap的主要内容,如果未能解决你的问题,请参考以下文章