1

我有一个反序列化这个 JSON 的 Java 数据结构:

{
  'level1 value1': {
    'level2 value1': {
      'level3 value1': [ "25", "45", "78" ],
      // ...
      'level3 valueN': [ "59", "17", "42" ]
    },
    // ...
    'level2 valueN': {
      'level3 value1': [ "34", "89", "54" ],
      // ...
      'level3 valueN': [ "45", "23", "23" ]
    },
  },
  // ...
  'level1 valueN': {
    // ...
  }
}

在 Java 中,这变成:

Map<String, Map<String, Map<String, List<String>>>> data;

当然,层数是任意的,所以我不能真正在变量声明中嵌套集合。我正在做的是:

void traverse(Map<String, ?> children) {
  for (Map.Entry<String, ?> node : data.entrySet()) {
    if (node.getValue() instanceof Map) {
      doSomethingWithNonLeafNode((Map<String, Map<String, ?>>) node);
    } else if (node.getValue() instanceof List) {
      doSomethingWithLeafNode((Map <String, List<String>>) node);
    }
  }
}


void doSomethingWithNonLeafNode(Map <String, Map<String ?>> node) {
  // do stuff
}


void doSomethingWithLeafNode(Map <String, List<String>> node) {
  // do stuff
}

这显然 a) 使用了一些未经检查的强制转换,并且 b) 是丑陋的。我试图定义新类型来解决这个问题:

private interface Node extends Map<String, Map<String, ?>> {
}

private interface LeafNode extends Map<String, List<String>> {
}

// ...

    if (node.getValue() instanceof Map) {
      doSomethingWithNonLeafNode((Node) node);
    } else if (node.getValue() instanceof List) {
      doSomethingWithLeafNode((LeafNode) node);
    }

但是,这给了我一个运行时异常:

java.lang.ClassCastException: java.util.HashMap cannot be cast to com.foo.ReportDataProcessor$Node

我怎样才能以干净、无警告的方式做到这一点?

4

3 回答 3

1

定义新类型对您没有帮助,因为 JSON 反序列化器不知道您的 Node 和 LeafNode 接口,因此生成的具体集合对象无法实现它们。

由于您实际上并不事先知道映射的元素类型,因此没有强制执行编译时类型安全:您只能依赖运行时类型检查。因此,泛型只会让你的生活复杂化并使你的代码更丑陋,但不会给你带来任何好处。

所以我认为这里最糟糕的解决方案是删除泛型,在代码中使用非泛型MapList类型。

于 2012-03-14T10:08:21.107 回答
1

如果没有联合类型,我认为你不能很好地做到这一点。

如果您考虑一下,Map您正在处理的实际上可以包含两种类型的对象 - 或者是下面级别的另一个映射,或者是叶节点的字符串列表。因此,此映射的通用参数必须是映射和列表的某些通用超类型。最终,当您检测到您有一个叶节点并正在处理一个列表时,您将不得不强制转换。

于 2012-03-14T10:09:10.013 回答
1

对于更清洁的解决方案,您可能会忘记 Map 作为容器并创建一个对象来表示树,更好

public class Node{
   String text;
   List<String> data;
   List<Node> children; 
}

您可以使用此树结构实现使用 Map 填充树的当前方式。如果节点的子节点不为空,那么您不必考虑数据。

void traverse(Node node) {
  if(node.getChildren()!=null){
      doSomethingWithNonLeafNode(node);
      // Or for recursion if you need
      // List<Node> children=node.getChildren();
      // for(Node c:children){
      //    traverse(c);
      // }
  }else{
      doSomethingWithLeafNode(node);
  }
}
于 2012-03-14T10:24:01.870 回答