You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android下使用Jackson解析28MB大型JSON文件求助

解决OpenWeather城市JSON文件解析与分组问题

看起来你在处理大JSON文件和数据分组时踩了几个典型的坑,我帮你梳理下问题并给出完整的解决方案:

你的代码里的核心问题

  1. JSON解析逻辑错误
    你逐行读取文件并累加字符串后,每次尝试把整个(不断变长的)JSON字符串解析成单个City对象,但你的JSON根结构是数组,不是单个对象,这会直接抛出解析异常。而且这种方式会把整个28MB文件加载到内存里,很容易触发内存溢出。

  2. 实体类Setter方法不符合规范
    Jackson依赖标准的JavaBean setter方法来映射属性,你的setCity和setCountry返回了String类型,而不是标准的void,这会导致Jackson无法正确识别并赋值属性。

  3. 数据分组逻辑错误
    你用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();
            }
        });
    }
}

关键技术点说明

  1. Jackson流式解析
    用MappingIterator可以逐个读取JSON数组中的元素,内存占用极低,非常适合处理几十MB级别的大JSON文件,避免一次性加载整个文件导致的内存压力。

  2. JavaBean规范
    Jackson默认遵循JavaBean规范:

    • 属性名和JSON字段名对应(比如JSON里的name对应实体类的name属性)
    • Setter方法是void setXxx(T value),Getter方法是T getXxx()
    • 必须有无参构造函数(你已经写了,这点没问题)
  3. Map分组技巧
    使用computeIfAbsent方法可以简化分组逻辑:如果Map中没有对应的key,就执行lambda表达式创建新的列表,然后直接添加元素,避免了繁琐的空判断和初始化操作。


内容的提问来源于stack exchange,提问作者Vivek Modi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:29:55