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

来源:互联网 发布:一句可爱到化的话 知乎 编辑:程序博客网 时间:2024/06/04 18:55

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 = 0E/TAG: str1jsub =  1



原创粉丝点击