Android ListView加载含空格JSON项点击崩溃问题及优化方案咨询
这个问题的核心原因很明确:你现在是把列表项toString()的输出当成JSON来解析,但默认的toString()生成的根本不是标准JSON格式——比如你看到的{nome=SPACE CRASH, cf=12345674},里面的键和值都没有双引号,当值包含空格时,JSON解析器会把SPACE误认为是一个新的键,直接触发语法错误,也就是你遇到的org.json.JSONException: Unterminated object。
字符串替换只是临时凑合用的办法,长期来看很容易踩坑(比如如果值里包含=或者,,替换逻辑会直接失效)。下面给你几个更可靠的最优解决方案:
方案1:保存原始结构化数据(推荐首选)
不要依赖toString()来传递数据,而是在Adapter的数据源中直接保存原始的JSONObject或者自定义的Model对象,点击列表项时直接获取这个对象,完全跳过字符串解析的步骤。
示例1:用自定义Model类(更符合Android开发规范)
首先定义对应JSON数据的Model类:
public class User { private String nome; private String cf; // 构造方法 public User(String nome, String cf) { this.nome = nome; this.cf = cf; } // Getter方法 public String getNome() { return nome; } public String getCf() { return cf; } // 如果需要转成标准JSON,用Gson/Jackson等序列化库 public String toStandardJson() { return new Gson().toJson(this); } }
然后初始化Adapter的数据源:
// 假设你从原始JSON数组解析出数据 List<User> userList = new ArrayList<>(); JSONArray jsonArray = new JSONArray(yourRawJsonString); for (int i = 0; i < jsonArray.length(); i++) { JSONObject obj = jsonArray.getJSONObject(i); userList.add(new User(obj.optString("nome"), obj.optString("cf"))); } // 设置ListView的Adapter(比如ArrayAdapter) ArrayAdapter<User> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, userList); listView.setAdapter(adapter);
最后处理点击事件:
listView.setOnItemClickListener((parent, view, position, id) -> { // 直接获取原始的User对象,不需要解析字符串 User clickedUser = userList.get(position); // 直接使用属性,比如: String nome = clickedUser.getNome(); String cf = clickedUser.getCf(); // 如果确实需要JSON对象,用Model的toStandardJson方法生成标准JSON再解析 try { JSONObject jsonObj = new JSONObject(clickedUser.toStandardJson()); // 后续逻辑 } catch (JSONException e) { e.printStackTrace(); } });
示例2:直接保存JSONObject
如果不想定义Model类,也可以直接把解析后的JSONObject存在列表里:
List<JSONObject> jsonList = new ArrayList<>(); JSONArray jsonArray = new JSONArray(yourRawJsonString); for (int i = 0; i < jsonArray.length(); i++) { jsonList.add(jsonArray.getJSONObject(i)); } // 设置Adapter时,重写getView来显示数据 listView.setAdapter(new BaseAdapter() { @Override public int getCount() { return jsonList.size(); } @Override public Object getItem(int position) { return jsonList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false); } TextView textView = convertView.findViewById(android.R.id.text1); JSONObject obj = jsonList.get(position); textView.setText(obj.optString("nome") + " | " + obj.optString("cf")); return convertView; } }); // 点击事件直接拿JSONObject listView.setOnItemClickListener((parent, view, position, id) -> { JSONObject clickedObj = jsonList.get(position); // 直接使用obj里的字段,比如clickedObj.optString("nome") });
方案2:生成标准JSON格式的toString()(次选)
如果因为某些原因必须用toString()来传递数据,那一定要重写对象的toString()方法,让它输出标准JSON字符串,而不是默认的键值对格式。
比如在自定义Model类里重写toString:
@Override public String toString() { // 用Gson生成标准JSON,确保键和值都带双引号 return new Gson().toJson(this); }
这样点击时拿到的toString结果就是{"nome":"SPACE CRASH", "cf":"12345674"},转JSONObject就不会出错了。
为什么不推荐字符串替换?
你的临时替换方案虽然能解决空格的问题,但如果JSON值里出现=、,或者其他特殊字符,替换逻辑会直接失效,而且维护起来非常麻烦。比如如果nome的值是Hello=World, Test,替换后的字符串还是不符合JSON规范,依然会崩溃。
所以最稳妥的方式还是直接保存原始的结构化数据,避免从非标准字符串中解析JSON。
内容的提问来源于stack exchange,提问作者Marcus J.Kennedy




