基于Jackson实现嵌套Map过滤保留结构:筛选Hit为True的电影
筛选JSON中
hit为true的电影(保留层级结构) 问题说明
需要从层级结构的JSON文件中筛选出所有hit字段为true的电影条目,同时保留原有的地区(Bollywood/Hollywood)和类型(Action/Romance)层级结构,但当前编写的Jackson解析代码报错,无法实现需求。
示例JSON内容
{ "Bollywood": { "Romance": { "hit": false, "name": "Prithvi Raj" }, "Action": { "hit": true, "name": "3 Idiots" } }, "Hollywood": { "Romance": { "hit": false, "name": "Intimacy" }, "Action": { "hit": true, "name": "Seven Samurai" } } }
当前错误代码
import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.*; import java.util.stream.Collectors; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; public class TestJson { public static void main(String[] args) { try { // 创建ObjectMapper实例 ObjectMapper mapper = new ObjectMapper(); // 将JSON文件转换为Map Map<?, ?> map = mapper.readValue(Paths.get("movie.json").toFile(), Map.class); map = map.entrySet().stream().collect( Collectors.toMap( Map.Entry::getKey, entry -> entry.getValue() .entrySet() .stream() .filter(subEntry -> subEntry.getValue().equals(true)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) ); map.forEach((k,v) -> System.out.println("Key : " + k + ", Value : " + v)); } catch (Exception ex) { ex.printStackTrace(); } } }
报错信息
TestJson.java:46: error: cannot find symbol .entrySet() ^ symbol: method entrySet() location: class Object TestJson.java:49: error: incompatible types: cannot infer type-variable(s) T,K#1,U .collect(Collectors.toMap(Map.Entry::getKey, ^ (argument mismatch; invalid method reference method getKey in interface Entry<K#2,V> cannot be applied to given types required: no arguments found: Object reason: actual and formal argument lists differ in length) where T,K#1,U,K#2,V are type-variables: T extends Object declared in method <T,K#1,U>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>) K#1 extends Object declared in method <T,K#1,U>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>) U extends Object declared in method <T,K#1,U>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>) K#2 extends Object declared in interface Entry V extends Object declared in interface Entry 2 errors
期望输出结果
{ "Bollywood": { "Action": { "hit": true, "name": "3 Idiots" } }, "Hollywood": { "Action": { "hit": true, "name": "Seven Samurai" } } }
错误原因分析
- 类型转换问题:将JSON转为
Map<?, ?>后,entry.getValue()的类型是Object,无法直接调用entrySet()方法,必须先明确其为Map类型。 - 筛选条件错误:原代码判断
subEntry.getValue().equals(true),但subEntry.getValue()是包含hit和name的对象,不是直接的布尔值,需要提取hit字段的值再判断。
解决方案
方案一:使用泛型Map明确层级结构
通过指定泛型类型,避免Object类型的转换问题,同时修正筛选逻辑:
import java.io.File; import java.util.Map; import java.util.stream.Collectors; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class TestJson { public static void main(String[] args) { try { ObjectMapper mapper = new ObjectMapper(); // 明确JSON的层级泛型:地区 -> 电影类型 -> 电影属性 Map<String, Map<String, Map<String, Object>>> movieMap = mapper.readValue( new File("movie.json"), new TypeReference<Map<String, Map<String, Map<String, Object>>>>() {} ); // 层级筛选:保留每个地区下hit为true的电影类型 Map<String, Map<String, Map<String, Object>>> filteredMap = movieMap.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, regionEntry -> regionEntry.getValue().entrySet().stream() // 提取hit字段并判断是否为true .filter(typeEntry -> (Boolean) typeEntry.getValue().get("hit")) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue )) )); // 格式化输出结果 mapper.enable(SerializationFeature.INDENT_OUTPUT); System.out.println(mapper.writeValueAsString(filteredMap)); } catch (Exception ex) { ex.printStackTrace(); } } }
方案二:使用Jackson JsonNode处理(更灵活)
利用Jackson的JsonNodeAPI直接操作JSON节点,无需依赖泛型,适合结构复杂的JSON:
import java.io.File; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.SerializationFeature; public class TestJson { public static void main(String[] args) { try { ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(new File("movie.json")); // 创建空节点存储筛选结果 ObjectNode filteredRoot = mapper.createObjectNode(); // 遍历每个地区节点 rootNode.fields().forEachRemaining(regionEntry -> { String regionName = regionEntry.getKey(); JsonNode regionNode = regionEntry.getValue(); ObjectNode filteredRegion = mapper.createObjectNode(); // 遍历地区下的所有电影类型 regionNode.fields().forEachRemaining(typeEntry -> { JsonNode movieNode = typeEntry.getValue(); // 判断hit字段是否为true if (movieNode.get("hit").asBoolean()) { filteredRegion.set(typeEntry.getKey(), movieNode); } }); // 仅保留包含有效电影的地区 if (!filteredRegion.isEmpty()) { filteredRoot.set(regionName, filteredRegion); } }); // 格式化输出 mapper.enable(SerializationFeature.INDENT_OUTPUT); System.out.println(mapper.writeValueAsString(filteredRoot)); } catch (Exception ex) { ex.printStackTrace(); } } }
方案说明
- 方案一通过泛型明确了JSON的层级结构,代码逻辑清晰,适合结构固定的JSON场景。
- 方案二使用
JsonNode操作节点,无需提前定义泛型,处理动态结构的JSON更灵活,可读性也更强。
内容的提问来源于stack exchange,提问作者isilia
相关产品推荐
相关产品推荐

