源生Android Json数据解析的深入理解和使用

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

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闽农qq:994955138

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值