json数据基础的解析方法想必已经有很多的资料了,我们今天就不讲json数据解析的基础知识了,请有不懂的同学自行补充。
今天我们从一个问题例子出发,来扒一扒json数据解析的源代码,从而去理解他的工作方式,这样我们才能更好的去使用它,才能避免解析失败和异常的情况,还有就是
不用再纠结于json字符串的格式
在安卓中执行如下代码
String js = "{\"s\":\"2\",\"i\":3,\"b\":\"true\",\"c\":字符}"; try { JSONObject jsonObject = new JSONObject(js); String s = jsonObject.optString("s"); String i = jsonObject.optString("i"); boolean b = jsonObject.optBoolean("b"); Log.e("TAG", " s = " + s + " i = " + i + " b = " + b + " c = " + jsonObject.optString("c")); }catch (Exception e){ e.printStackTrace(); }
控制台的log是
E/TAG: s = 2 i = 3 b = true c = 字符
我们发现"s"的value带了双引号,"b"的value也带了双引号,然而"i"和"c"的value没有带双引号。结果却是都解析得到了正确的结果。
这是否说明json中对value的基础类型有没有进行识别呢?我们来用代码说话
首先是创建一个JSONObject 对象,把一个字符串解析成json对象的源码如下
private final LinkedHashMap<String, Object> nameValuePairs;
public JSONObject(String json) throws JSONException { this(new JSONTokener(json)); }
public JSONTokener(String in) { // consume an optional byte order mark (BOM) if it exists if (in != null && in.startsWith("\ufeff")) {//去掉bom头 in = in.substring(1); } this.in = in; }
public JSONObject(JSONTokener readFrom) throws JSONException { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONObject and then steal the data from that. */ Object object = readFrom.nextValue(); if (object instanceof JSONObject) { this.nameValuePairs = ((JSONObject) object).nameValuePairs; } else { throw JSON.typeMismatch(object, "JSONObject"); } }
可以看出首先通过JSONTokener类,将字符串解析成JSONObject对象。然后将json的键值对通过Map<String,Object>的形式
存在JSONObject的内部成员变量nameValuesPairs中.
所以实质上,json的键值对在安卓里被存在了一个Map<String,Object>里面,所以JSONObject的取值和存入值源码如下
public String getString(String name) throws JSONException { Object object = get(name); String result = JSON.toString(object); if (result == null) { throw JSON.typeMismatch(name, object, "String"); } return result; }
get(name)和JSON.toString(object)又是执行了什么呢?源码如下
public Object get(String name) throws JSONException { Object result = nameValuePairs.get(name); if (result == null) { throw new JSONException("No value for " + name); } return result; }
static String toString(Object value) { if (value instanceof String) { return (String) value; } else if (value != null) { return String.valueOf(value); } return null; }
也就是说,在json里面你调用json.getString("key")的时候,实质上是去json内部的nameValuePairs中取map里面的对应值,
然后又根据你数据类型的需要帮你做了数据类型转换。
所以可以得出结论,在json中,value有没有引号并不是很重要,最终的数据类型取决于你调用了getString()还是getInt()。
你调用get方法的类型决定了你收到额数据类型。
所以当你只知道key的名字,而不知道key的类型的时候大可以全部用String类型来做中转。
我们从JSONObject的源码也可以看到,当我们调用jsonObject.getString("key")的时候,如果json里面不存在这个key,那么
就会抛出异常导致程序崩溃,这不是我们想要的。
所以我们看到了
public String optString(String name) { return optString(name, ""); } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary, or {@code fallback} if no such mapping exists. */ public String optString(String name, String fallback) { Object object = opt(name); String result = JSON.toString(object); return result != null ? result : fallback; }
我们可以看到,在调用optString("key")的时候,就算key不存在,也不会抛异常,而是返回一个空的"",或者我们还可以
填一个key不存在的时候value的默认值。这就有助于我们判断是key不存在还是其他问题,比直接抛异常要好的多。
以上是基础数据类型的解析,那么要是不是基础数据类型类呢?
比如JSONObject里面包含JSONObject的情况呢?
看如下列子
String st= "{\"ad\":\"\"}"; String st1= "{\"ad\":{}}"; try { JSONObject stj = new JSONObject(st); JSONObject str1j = new JSONObject(st1); JSONObject stjsub = stj.optJSONObject("ad"); JSONObject str1jsub = str1j.optJSONObject("ad"); Log.e("TAG","stjsub == null ? " + (stjsub == null) + " str1jsub == null ? " + (str1jsub == null)); }catch (Exception e){ e.printStackTrace(); }
Log是 E/TAG: stjsub == null ? true str1jsub == null ? false
可见这两种方式是不同的。
我们用源码来解释一下
public JSONObject optJSONObject(String name) { Object object = opt(name); return object instanceof JSONObject ? (JSONObject) object : null; }
public Object opt(String name) { return nameValuePairs.get(name); }
在获取JSONObject对象的时候,就不像基础数据类型的获取那样存在数据类型转换了。而是直接就判断获取到的是不是JSONObject
的实例,如果不是就直接返回null;
那么 nameValuePairs.get(name);获取到的值是怎么来的呢?我们来看看 nameValuePairs 键值对的存储的关键代码
public Object nextValue() throws JSONException { int c = nextCleanInternal(); switch (c) { case -1: throw syntaxError("End of input"); case '{': return readObject(); case '[': return readArray(); case '\'': case '"': return nextString((char) c); default: pos--; return readLiteral(); } }
从上面可以看出,只有value中存在"{"字符,才会解析成JSONObject,没有带这个字符的都不能解析成JSONObject。
所以Log里面会出现 stjsub == null ? true。
这就告诉我们,在解析JSONObject的时候返回为null的一定是没数据,但是解析出来JSONObject不为null的,不一定就有数据.
还有可能是空JSON对象{}。所以在调用解析结果的时候要先判断一下键值对的个数大于0个,即jsonObject.length()>0才能确
保不是空JSON对象。
如下两个
String st= "{\"ad\":{}}"; String st1= "{\"ad\":{\"key\":\"value\"}}"; try { JSONObject stj = new JSONObject(st); JSONObject str1j = new JSONObject(st1); JSONObject stjsub = stj.optJSONObject("ad"); JSONObject str1jsub = str1j.optJSONObject("ad"); Log.e("TAG","stjsub = " + stjsub.length()); Log.e("TAG","str1jsub = " + str1jsub.length()); }catch (Exception e){ e.printStackTrace(); }
Log是
E/TAG: stjsub = 0 E/TAG: str1jsub = 1