Android 关于用JSONObject的new JSONObject(String s)构建含有特殊符号的字符串时报错的解决方案

来源:互联网 发布:二叉树的遍历设计算法 编辑:程序博客网 时间:2024/06/07 03:24

 说明:转载请注明出处:http://blog.sina.com.cn/s/blog_821e2bb101013lv3.html

 最近做的一个项目需要在后台获取json形式的字符串然后用new JSONObject(Strings)方法构建成JSONObject对象,开发用的Android1.6没有什么问题,但是当在2.2及以上系统测试时会报错,报错显示当字符串有空格时无法构建JSONObject对象,刚开始以为机型问题,并没有太在意,后来发现很多机型都有问题,于是就不得不解决一下了。

 最开始百度、Google了很久一直没找到解决的办法,既然从外找不到解决方案,就只能从内下手,于是开始研究JSONObject这个类看看1.6与2.2有什么不同,找到不同点可能就找到了原因所在。

 先研究了下2.2的JSONObject类,new JSONObject(String s)的源码:

   publicJSONObject(String json)throwsJSONException {

         this(newJSONTokener(json));
  }
不难发现JSONObject(Strings)这个构造函数其实并没有做什么工作,只是直接调用了JSONObject的另一个构造函数:
publicJSONObject(JSONTokener readFrom)throwsJSONException {
         
         Objectobject =readFrom.nextValue();
         if(objectinstanceof JSONObject){
             this.nameValuePairs= ((JSONObject) object).nameValuePairs;
         }else{
             throwJSON.typeMismatch(object,"JSONObject");
         }
     }
这个方法也很简单只是调用了JSONToker类的nextValue方法然后把该方法得到的JSONToker的HanshMap对象nameValuePairs赋给JSONObject的nameValuePairs,所以重点在JSONToker的nextValue()方法。
nextValue()的源码:
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();
       }
    }
看nextValue似乎依然找不到原因,在深入研究,其调用的nextCleanInternal()方法:
 private int nextCleanInternal() throwsJSONException {
       while (pos < in.length()) {
           int c =in.charAt(pos++);
           Log.d("pos", "pos="+pos+" "+c+" "+in.length());
           switch (c) {
               case '\t':
               case '':
               case '\n':
               case '\r':
                   continue;
               case '/':
                   if (pos == in.length()) {
                       return c;
                   }
                   char peek = in.charAt(pos);
                   switch (peek) {
                       case '*':
                           // skip a
                           pos++;
                           int commentEnd = in.indexOf("*/", pos);
                           if (commentEnd == -1) {
                               throw syntaxError("Unterminated comment");
                           }
                           pos = commentEnd + 2;
                           continue;
                       case '/':
                           // skip a // end-of-line comment
                           pos++;
                           skipToEndOfLine();
                           continue;
                       default:
                           return c;
                   }
               case '#':
                   
                   skipToEndOfLine();
                   continue;
               default:
                   return c;
           }
       }
       return -1;
    }
看代码,不难发现有一个case ‘’,哈哈终于找到问题所在了。至于原因看到这里也应该都明白了吧,当字符串中有空格时,会直接跳出while循环,返回-1,这样在nextValue中会匹配case-1,自然就抛出异常了。
 
好了说了这么多该说解决方案了:
方案一:
研究了2.2后又看了下1.6发现1.6并没有对空格进行判断,附上1.6的nextValue()与nextClean()方法源码:
public Object nextValue() throws JSONException {
       char c = nextClean();
       String s;
       switch (c) {
           case '"':
           case '\'':
               return nextString(c);
           case '{':
               back();
               return new JSONObject(this);
           case '[':
               back();
               return new JSONArray(this);
       }
 
public char nextClean() throws JSONException {
       for (;;) {
           char c = next();
           if (c == '/') {
               switch (next()) {
               case '/':
                   do {
                       c = next();
                   } while (c != '\n' && c != '\r'&& c != 0);
                   break;
               case '*':
                   for (;;) {
                       c = next();
                       if (c == 0) {
                           throw syntaxError("Unclosed comment.");
                       }
                       if (c == '*') {
                           if (next() == '/') {
                               break;
                           }
                           back();
                       }
                   }
                   break;
               default:
                   back();
                   return '/';
               }
           } else if (c == '#') {
               do {
                   c = next();
               } while (c != '\n' && c != '\r'&& c != 0);
           } else if (c == 0 || c > ' ') {
               return c;
           }
       }
    }
所以方案一就是直接把1.6的org.json这个包复制到项目里,用这个包里的JSONObject而不是用系统的,不过放入项目时要改一下报名,貌似不改名不识别。
android1.6SDK源码及部分版本源码下载地址(别人总结的):
http://fu122.iteye.com/blog/534010
 
方案二:
第二中方案就是定制自己的JSONObject类,拿2.2来说,只需把JSONObject与JSONToker与JSON三个类放到项目中再注释掉nextCleanInternal()方法的相应case就OK了,当然使用的时候要用重写的这个JSONObject。
0 0