美文网首页
Json 一点细节 unicode 转中文。

Json 一点细节 unicode 转中文。

作者: David_zhou | 来源:发表于2023-01-30 12:01 被阅读0次

    在json的使用过程中忽然发现一个有意思的点,传入JSONObject构造函数的String 中有Unicode,但取出来之后却变成了对应的中文,于是就有了下面的探索之旅。

    json_1.png

    config是{"title":"\u70b9\u5e7f\u544a\u514d\u8d39\u542c\u4f1a\u5458\u6b4c","time":10}。
    从截图中可以看出在构造出的JsonObject中就已经完成转换,
    所以unicode 转成中文不是在getString 中处理,而是在config 参数传入Json的构造函数中转换号的。换句话说就是在构造JsonObject的时候就完成了。
    接下来就从构造函数开始看看,Json的构造函数比较简单:

     public JSONObject(@NonNull String json) throws JSONException {
            this(new JSONTokener(json));
        }
    

    Json的构造函数中会新建一个JSONTokener,这个时候新建的对象并没有将unicode 转成中文。


    json_2.png

    继续看JSONObject的构造函数,JSONTokener调用nextValue() 生成一个Object,这个时候生成的object 对象已经完成了unicode 转成中文的转换过程。


    json_3.png

    接下来不太好调试,只能硬看代码了,先从开始JSONTokener类的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();
            }
        }
    

    调用了nextCleanInternal()这个方法,它会从头到尾的逐个字符的解析,对于一些字符做一些处理。例如空格,换行,转义符等!
    这里先跳过,大概就是返回一个索引。这里我们是第一次调用nextValue() 方法,所以肯定是以 { 开头,所以直接看readObject()。

    private JSONObject readObject() throws JSONException {
          JSONObject result = new JSONObject(); // 新建一个JSONObject对象
    
          /* Peek to see if this is the empty object. */
          int first = nextCleanInternal();
          if (first == '}') {
              return result;
          } else if (first != -1) {
              pos--;
          }
    
          while (true) {
              Object name = nextValue();  
              if (!(name instanceof String)) { //对json的key 进行检查, 只能是非空的String 类型。
                  if (name == null) {
                      throw syntaxError("Names cannot be null");
                  } else {
                      throw syntaxError("Names must be strings, but " + name
                              + " is of type " + name.getClass().getName());
                  }
              }
    
              /*
               * Expect the name/value separator to be either a colon ':', an
               * equals sign '=', or an arrow "=>". The last two are bogus but we
               * include them because that's what the original implementation did.
               */
              int separator = nextCleanInternal();  // 此处又碰到了我们跳过的方法,😂 继续跳过,先简单理解返回一个位置索引。
              if (separator != ':' && separator != '=') {  // key 之后的分隔符必须是 : 或者 = ,分隔符是 :,比较常见,但什么情况下分隔符是 = ,此处有疑问!
                  throw syntaxError("Expected ':' after " + name);
              }
              if (pos < in.length() && in.charAt(pos) == '>') {
                  pos++;
              }
    
              result.put((String) name, nextValue());  // 此处再次调用了nextValue(),将key 和nextValue()的返回值存储在result 这个JsonObject 对象中
    
              switch (nextCleanInternal()) {
                  case '}':
                      return result;
                  case ';':
                  case ',':
                      continue;
                  default:
                      throw syntaxError("Unterminated object");
              }
          }
      }
    
    

    关键代码是 result.put((String) name, nextValue()); // 此处再次调用了nextValue(),将key 和nextValue()的返回值存储在result 这个JsonObject 对象中。不过此时是第二次调用nextValue(),而且读取的位置也从string 的开头,来到了key 后面分分隔符了。此时原始字符串的分隔符是 “ ,所以实际执行的代码是如下代码中的nextString()。

    public Object nextValue() throws JSONException {
            int c = nextCleanInternal();
            switch (c) {
         
                case '{':
                    return readObject();
                case '\'':
                case '"':
                    return nextString((char) c);
    
                default:
                    pos--;
                    return readLiteral();
            }
        }
    

    nextString() 的代码如下:

    public String nextString(char quote) throws JSONException {
    /*
    * For strings that are free of escape sequences, we can just extract
    * the result as a substring of the input. But if we encounter an escape
    * sequence, we need to use a StringBuilder to compose the result.
    */
    StringBuilder builder = null;

        /* the index of the first character not yet appended to the builder. */
        int start = pos;
    
        while (pos < in.length()) {
            int c = in.charAt(pos++);
            if (c == quote) {
                if (builder == null) {
                    // a new string avoids leaking memory
                    return new String(in.substring(start, pos - 1));
                } else {
                    builder.append(in, start, pos - 1);
                    return builder.toString();
                }
            }
    
            if (c == '\\') {
                if (pos == in.length()) {
                    throw syntaxError("Unterminated escape sequence");
                }
                if (builder == null) {
                    builder = new StringBuilder();
                }
                builder.append(in, start, pos - 1);
                builder.append(readEscapeCharacter());
                start = pos;
            }
        }
    
        throw syntaxError("Unterminated string");
    }
    

    比较关键的方法是readEscapeCharacter,继续跟进去看看,关键代码如下:

    private char readEscapeCharacter() throws JSONException {
           char escaped = in.charAt(pos++);
           switch (escaped) {
               case 'u':
                   if (pos + 4 > in.length()) {
                       throw syntaxError("Unterminated escape sequence");
                   }
                   String hex = in.substring(pos, pos + 4);
                   pos += 4;
                   try {
                       return (char) Integer.parseInt(hex, 16);
                   } catch (NumberFormatException nfe) {
                       throw syntaxError("Invalid escape sequence: " + hex);
                   }
    
           }
       }
    

    此时大概就看出,此处就将unicode转成中文,取出四位char, 然后转成Integter. 最后再转成Integer对应的char.

    还有很多疑问,需要补下Json 的解析以及对应的细节

    android基础---->JSON数据的解析

    相关文章

      网友评论

          本文标题:Json 一点细节 unicode 转中文。

          本文链接:https://www.haomeiwen.com/subject/duemhdtx.html