一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - 詳談Java中net.sf.json包關于JSON與對象互轉的坑

詳談Java中net.sf.json包關于JSON與對象互轉的坑

2021-02-26 13:18OKevin JAVA教程

下面小編就為大家分享一篇Java中net.sf.json包關于JSON與對象互轉的坑,具有很好的參考價值,希望對大家有所幫助

在web開發過程中離不開數據的交互,這就需要規定交互數據的相關格式,以便數據在客戶端與服務器之間進行傳遞。數據的格式通常有2種:1、xml;2、json。通常來說都是使用json來傳遞數據。本文正是介紹在java中json與對象之間互相轉換時遇到的幾個問題以及相關的建議。

首先明確對于json有兩個概念:

json對象(javascript object notation,javascript對象表示法)。這看似只存是位javascript所定制的,但它作為一種語法是獨立于語言以及平臺的。只是說通常情況下我們在客戶端(瀏覽器)向服務器端傳遞數據時,使用的是json格式,而這個格式是用于表示javascript對象。它是由一系列的“key-value”組成,如 {“id”: 1, “name”: “kevin”},這有點類似map鍵值對的存儲方式。在java中所述的json對象,實際是指的jsonobject類,這在各個第三方的jsonjar包中通常都以這個名字命名,不同jar包對其內部實現略有不同。

json字符串。json對象和json字符串之間的轉換是序列化與反序列化的過程,這就是好比java對象的序列化與反序列化。在網絡中數據的傳遞是通過字符串,或者是二進制流等等進行的,也就是說在客戶端(瀏覽器)需要將數據以json格式傳遞時,此時在網絡中傳遞的是字符串,而服務器端在接收到數據后當然也是字符串(string類型),有時就需要將json字符串轉換為json對象再做下一步操作(string類型轉換為jsonobject類型)。

以上兩個概念的明確就基本明確了json這種數據格式,或者也稱之為json語法。java中對于json的jar包有許多,最最“常用”的是“net.sf.json”提供的jar包了,本文要著重說的就是這個坑包,雖然坑,卻有著廣泛的應用。其實還有其他優秀的json包供我們使用,例如阿里號稱最快的json包——fastjson,還有谷歌的gson,還有jackson。盡量,或者千萬不要使用“net.sf.json”包,不僅有坑,而且已經很老了,老到都沒法在idea里下載到源碼,maven倉庫里顯示它2010年在2.4版本就停止更新了。下面就談我已知的“net.sf.json”的2個bug(我認為這是bug),以及這2個bug是如何產生的。

java中的json坑包——net.sf.json

1. 在java對象轉換json對象時,get開頭的所有方法會被轉換

這是什么意思呢,例如現有以下java對象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package sfjson;
 
import java.util.list;
 
/**
 * created by kevin on 2017/12/1.
 */
public class student {
  private int id;
  private list<long> courseids;
 
  public int getid() {
    return id;
  }
 
  public void setid(int id) {
    this.id = id;
  }
 
  public list<long> getcourseids() {
    return courseids;
  }
 
  public void setcourseids(list<long> courseids) {
    this.courseids = courseids;
  }
 
  public string getsql() {    //此類中獲取sql語句的方法,并沒有對應的屬性字段
    return "this is sql.";
  }
}

在我們將student對象轉換成json對象的時候,希望轉換后的json格式應該是:

?
1
2
3
4
{
   "id": 1,
   "courseids": [1, 2, 3]
 }

然而在使用“net.sf.json”包的jsonobject json = jsonobject.fromobject(student); api轉換后的結果卻是:

詳談Java中net.sf.json包關于JSON與對象互轉的坑

也就是說可以猜測到的是,“net.sf.json”獲取java對象中public修飾符get開頭的方法,并將其后綴定義為json對象的“key”,而將get開頭方法的返回值定義為對應key的“value”,注意是public修飾符get開頭的方法,且有返回值。

我認為這是不合理的轉換規則。如果我在java對象中定義了一個方法,僅僅因為這個方法是“get”開頭,且有返回值就將其作為轉換后json對象的“key-value”,那豈不是暴露出來了?或者在返回給客戶端(瀏覽器)時候就直接暴露給了前端的console控制臺?作者規定了這種轉換規則,我想的大概原因是:既然你定義為了public方法,且命名為get,那就是有意將此方法暴露出來讓調用它的客戶端有權獲取。但我仍然認為這不合理,甚至我定義它是一個bug。我這么定義也許也不合理,因為據我實測發現,不僅是“net.sf.json”包會按照這個規則進行轉換,fastjson和jackson同樣也是照此規則,唯獨谷歌的gson并沒有按照這個規則進行對象向json轉換。

通過jsonobject json = jsonobject.fromobject(student);將構造好的student對象轉換為json對象,student如上文所述。 進入此方法后會繼續調用fromobject(object, jsonconfig)的重載方法,在此重載方法中會通過instanceof判斷待轉換的object對象是否是枚舉、注解等類型,這些特殊類型會有特別的判斷方法。在這里是一個普通的java pojo對象,所以會進入到_fromobject(object, jsonconfig),在這個方法中會有一些判斷,而最后則通過調用defaultbeanprocessing創建json對象。這個方法是關鍵,在里面還繼續會通過propertyutils.getpropertydescriptors(bean)方法獲取“屬性描述符”,實際上就是獲取帶get的方法,它在這里封裝成了propertydescriptor。這student這個類中會獲取4個,分別是:getclass、getid、getcourseids、getsql。

詳談Java中net.sf.json包關于JSON與對象互轉的坑

其實propertydescriptor封裝得已經很詳細了,什么讀寫方法都已經賦值了。

詳談Java中net.sf.json包關于JSON與對象互轉的坑

例如這個getsql方法已經被解析成了上圖的propertydescriptor。之后的通過這個類將一些方法過濾掉,例如getclass方法不是pojo中的方法,所以并不需要將它轉換成json對象。而propertydescriptor的獲取是通過beaninfo#getpropertydescriptors,而beaninfo的獲取則又是通過new introspector(beanclass, null, use_all_beaninfo).getbeaninfo();不斷深入最后就會到達如下方法。

?
1
2
3
4
5
private beaninfo getbeaninfo() throws introspectionexception {
  
  methoddescriptor mds[] = gettargetmethodinfo();  //這個方法中會調用getpublicdeclaredmethods,可以看到確實是查找public方法,而且是所有public方法,包括wait等
  propertydescriptor pds[] = gettargetpropertyinfo();  //按照一定的規則進行過濾,過濾規則全在這個方法里了,就是選擇public修飾符帶有get前綴和返回值的方法
  

對net.sf.json的源碼簡要分析了一下,發現確實如猜想的那樣,具體的源碼比較多篇幅有限需自行查看跟蹤。

2. 在json對象轉換java對象時,list<long>會出現轉換錯誤

標題一句話解釋不清楚,這個問題,我很確定地認為它是一個bug。

現在有{"id": 1, "courseids": [1,2,3]}的json字符串,需要將它轉換為上文中提到的student對象,在student對象中有int和list<long>類型的兩個屬性字段,也就是說這個json字符串應該轉換為對應的數據類型。

?
1
2
3
string json = "{\"id\": 1, \"courseids\": [1,2,3]}";
student student = (student) jsonobject.tobean(jsonobject.fromobject(json), student.class);
system.out.println(student.getcourseids().get(0) instanceof long);

 

上面的輸出結果應該是true,然而遺憾的是卻是false。準確來說在編譯時是long型,而在運行時卻是integer。這不得不說就是一個坑了,另外三個json包都未出現這種錯誤。所以我確定它是一個bug。來看看這個bug在net.sf.json是怎么發生的,同樣需要自行對比源碼進行查看。我在打斷點debug不斷深入的時候發現了net.sf.json對于整型數據的處理時,發現了這個方法numberutils#createnumber,這個類是從字符串中取出數據時判斷它的數據類型,本意是想如果數字后面帶有“l”或“l”則將其處理為long型,從這里來看最后的結果應該是對的啊。

?
1
2
3
4
5
6
7
8
9
10
11
case 'l':
case 'l':
  if (dec == null && exp == null && (numeric.charat(0) == '-' && isdigits(numeric.substring(1)) || isdigits(numeric))) {
    try {
      return createlong(numeric);
    } catch (numberformatexception var11) {
      return createbiginteger(numeric);
    }
  } else {
    throw new numberformatexception(str + " is not a valid number.");
  }

的確到目前為止net.sf.json通過數字后的標識符準確地判斷了數據類型,問題出就出在獲得了這個值以及它的數據類型后需要將它存入jsonobject中,而存入的過程中有jsonutils#transformnumber這個方法的存在,這個方法的存在,至少在目前看來純屬畫蛇添足。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static number transformnumber(number input) {
  if (input instanceof float) {
    return new double(input.tostring());
  } else if (input instanceof short) {
    return new integer(input.intvalue());
  } else if (input instanceof byte) {
    return new integer(input.intvalue());
  } else {
    if (input instanceof long) {
      long max = new long(2147483647l);
      if (input.longvalue() <= max.longvalue() && input.longvalue() >= -2147483648l) {  //就算原類型是long型,但是只要它在integer范圍,那么就最終還是會轉換為integer。
        return new integer(input.intvalue());
      }
    }
 
    return input;
  }
}

上面的這段代碼很清晰的顯示了元兇所在,不論是long型(integer范圍內的long型),包括byte、short都會轉換為integer。尚不明白這段代碼的意義在哪里。前面又要根據數字后的字母確定準確的數據類型,后面又要將準確的數據類型轉換一次,這就導致了開頭提到的那個bug。這個問題幾乎是無法回避,所以最好的辦法就是不要用。

這兩個坑是偶然間發現,建議還是不要使用早已沒有維護的net.sf.json的json包,另外有一點,net.sf.json包對json格式的校驗并不那么嚴格,如果這樣的格式“{"id": 1, "courseids": "[1,2,3]"}”,在其他三個包是會拋出異常的,但net.sf.json則不會。

以上這篇詳談java中net.sf.json包關于json與對象互轉的坑就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/yulinfeng/archive/2017/12/03/7967603.html

延伸 · 閱讀

精彩推薦
  • JAVA教程10分鐘帶你了解DevOps工具

    10分鐘帶你了解DevOps工具

    上周,我的一些非常資深的同事和我本人評論了有多少新的DevOps工具正在涌現,以及每天如何越來越難以追蹤它們以及它們在世界上的位置。...

    今日頭條5262020-10-13
  • JAVA教程Java Swing樹狀組件JTree用法實例詳解

    Java Swing樹狀組件JTree用法實例詳解

    這篇文章主要介紹了Java Swing樹狀組件JTree用法,結合具體實例形式分析了Swing組件JTree構成樹狀列表的節點設置與事件響應,以及自定義圖形節點的相關操作技...

    SunnyYoona5542021-02-07
  • JAVA教程springboot DTO字符字段與日期字段的轉換問題

    springboot DTO字符字段與日期字段的轉換問題

    這篇文章主要介紹了springboot DTO字符字段與日期字段的轉換問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,...

    張占嶺3182020-07-18
  • JAVA教程SpringData關鍵字查詢實現方法詳解

    SpringData關鍵字查詢實現方法詳解

    這篇文章主要介紹了SpringData關鍵字查詢實現方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以...

    鼓搗貓膩2042020-08-26
  • JAVA教程Hibernate緩存詳解

    Hibernate緩存詳解

    本文主要介紹了Hibernate緩存的相關知識。具有很好的參考價值,下面跟著小編一起來看下吧 ...

    MSTK4662020-08-02
  • JAVA教程理解Java垃圾回收

    理解Java垃圾回收

    這篇文章主要幫助大家理解Java垃圾回收,通過實例學習java垃圾回收,什么是垃圾回收,感興趣的小伙伴們可以參考一下 ...

    趙杰A-1241762020-04-13
  • JAVA教程Jenkins遷移job插件Job Import Plugin流程詳解

    Jenkins遷移job插件Job Import Plugin流程詳解

    這篇文章主要介紹了Jenkins遷移job插件Job Import Plugin流程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    shookm2742020-08-28
  • JAVA教程Spring MVC url提交參數和獲取參數

    Spring MVC url提交參數和獲取參數

    本文重要講述通過url提交參數和獲取參數的具體操作與實現。具有很好的參考價值。下面跟著小編一起來看下吧...

    Hoking4672020-09-07
主站蜘蛛池模板: 免费岛国 | 日韩毛片免费线上观看 | bl文全肉高h湿被灌尿 | 97导航| 久久强奷乱码老熟女 | 手机看片日韩1024你懂的首页 | 免费我看视频在线观看 | 成人免费在线视频观看 | 久久久久国产一级毛片高清片 | 天堂樱桃bt在线www | 日本高h | 999精品视频这里只有精品 | 欧美一级在线 | 97国产蝌蚪视频在线观看 | 亚洲激情欧美 | 欧美一级v片 | 三极片在线观看 | 天天做天天爱天天爽综合网 | 亚洲网站在线播放 | 国产特黄一级一片免费 | 干露露视频 性感写真 | 国产福利免费看 | 久久精麻豆亚洲AV国产品 | 91尤物在线视频 | 国产香蕉一区二区精品视频 | 无码日韩精品一区二区免费 | 亚洲qvod图片区电影 | 国产视频二 | 亚洲天堂在线视频播放 | katsuniav在线播放| 男人猛戳女人下部30分钟 | 2019nv天堂香蕉在线观看 | 91免费精品国自产拍在线不卡 | 亚洲精品一区二区观看 | 啪啪国产视频 | 紧身裙女教师miad711在线 | 天天爱天天做天天爽天天躁 | 国产免费午夜 | 青青草原网 | 成年人视频免费在线播放 | 欧美色图亚洲 |