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


 
 
 
 


发布了63 篇原创文章 · 获赞 41 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/MarketAndTechnology/article/details/76889479