Android下使用Jackson解析28MB大型JSON文件求助
看起来你在处理大JSON文件和数据分组时踩了几个典型的坑,我帮你梳理下问题并给出完整的解决方案:
你的代码里的核心问题
JSON解析逻辑错误
你逐行读取文件并累加字符串后,每次尝试把整个(不断变长的)JSON字符串解析成单个City对象,但你的JSON根结构是数组,不是单个对象,这会直接抛出解析异常。而且这种方式会把整个28MB文件加载到内存里,很容易触发内存溢出。实体类Setter方法不符合规范
Jackson依赖标准的JavaBean setter方法来映射属性,你的setCity和setCountry返回了String类型,而不是标准的void,这会导致Jackson无法正确识别并赋值属性。数据分组逻辑错误
你用Map<String,String>来存储,这意味着每个国家只能保留最后一个解析到的城市,因为put方法会覆盖相同key的value。正确的做法应该用Map<String, List<String>>来存储每个国家对应的城市列表。
修正后的完整代码
第一步:修复City实体类
@JsonIgnoreProperties(ignoreUnknown = true) public class City { private String name; private String country; public City() {} // 标准的Setter方法(void返回类型) public void setName(String name) { this.name = name; } public void setCountry(String country) { this.country = country; } // 保持你的get方法,也可以改成更规范的getName(),两种方式Jackson都能识别 public String getCity() { return name; } public String getCountry() { return country; } }
第二步:优化MainActivity的解析与分组逻辑
针对大文件,我们用Jackson的ObjectReader来流式解析JSON数组,这样不需要一次性加载整个文件到内存,避免内存溢出:
public class MainActivity extends AppCompatActivity { // 修改为Map<String, List<String>>,存储国家对应的城市列表 Map<String, List<String>> countryCityMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.B1).setOnClickListener(v -> { countryCityMap = new TreeMap<>(); ObjectMapper mapper = new ObjectMapper(); try (InputStream is = getAssets().open("city.list.json"); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { // 创建读取数组的ObjectReader,指定要解析的实体类 ObjectReader reader = mapper.readerFor(City.class); MappingIterator<City> iterator = reader.readValues(br); while (iterator.hasNext()) { City city = iterator.next(); String country = city.getCountry(); String cityName = city.getCity(); // 处理分组:如果国家不存在,创建新列表;否则添加到已有列表 countryCityMap.computeIfAbsent(country, k -> new ArrayList<>()).add(cityName); Log.e("City", country + " " + cityName); } // 解析完成后,可以在这里验证结果,比如打印某个国家的城市数量 Log.e("Result", "美国城市数量:" + countryCityMap.getOrDefault("US", Collections.emptyList()).size()); } catch (IOException ex) { ex.printStackTrace(); Toast.makeText(this, "解析失败:" + ex.getMessage(), Toast.LENGTH_LONG).show(); } }); } }
关键技术点说明
Jackson流式解析
用MappingIterator可以逐个读取JSON数组中的元素,内存占用极低,非常适合处理几十MB级别的大JSON文件,避免一次性加载整个文件导致的内存压力。JavaBean规范
Jackson默认遵循JavaBean规范:- 属性名和JSON字段名对应(比如JSON里的
name对应实体类的name属性) - Setter方法是
void setXxx(T value),Getter方法是T getXxx() - 必须有无参构造函数(你已经写了,这点没问题)
- 属性名和JSON字段名对应(比如JSON里的
Map分组技巧
使用computeIfAbsent方法可以简化分组逻辑:如果Map中没有对应的key,就执行lambda表达式创建新的列表,然后直接添加元素,避免了繁琐的空判断和初始化操作。
内容的提问来源于stack exchange,提问作者Vivek Modi

