2012-03-14 57 views
1

我有一个Java数据结构,其从deserialising此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 
} 

这显然是一个)使用一些未经检查的强制类型转换,和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 

我怎样才能做到这一点在清洁,无警告的时尚?

回答

1

定义新类型不会帮助您,因为JSON解串器不知道您的节点& LeafNode接口,因此生成的具体集合对象无法实现它们。由于您事先并未真正了解您的地图的元素类型,所以没有编译时安全性需要执行:您只能依赖运行时类型检查。因此泛型只会让你的生活复杂化,并使你的代码更加丑陋,但不会给你带来任何好处。

所以我认为这里最简单的解决方案是放弃泛型,在代码中使用非常规MapList类型。

+0

是的,我认为“最不可能的”必须在这里赢得。我认为Java对于这类事情并不是最好的语言。 – 2012-03-14 14:44:31

1

我不认为你可以做到这一点很好,没有工会类型。

如果你考虑一下,你正在处理的Map实际上可以包含两种类型的对象 - 要么是下面的级别的另一个映射,要么是叶节点的字符串列表。因此,此映射的泛型参数必须是映射和列表的一些常见超类型。最终,当你检测到你有一个叶节点并正在处理一个列表时,你将会有来投射。

1

对于一个清晰的解决方案,你可以约地图忘记作为容器,并创建一个对象来表示树,更好

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

而且可以实现填充使用地图树到使用的电流方式这棵树结构。如果Node的孩子不是空的,那么你不必考虑数据。

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); 
    } 
} 
+0

'扩展HashMap >'不会有什么区别:仍然会有类抛出异常。 – 2012-03-14 14:59:45

+0

@PéterTörök是的,我采取了另一种方式:)他可以将节点对象转换为HashMap而不是HashMap转换为节点。 – 2012-03-14 17:23:56