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

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|JavaScript|易語(yǔ)言|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - 詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑

詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑

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

下面小編就為大家分享一篇Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助

在web開(kāi)發(fā)過(guò)程中離不開(kāi)數(shù)據(jù)的交互,這就需要規(guī)定交互數(shù)據(jù)的相關(guān)格式,以便數(shù)據(jù)在客戶(hù)端與服務(wù)器之間進(jìn)行傳遞。數(shù)據(jù)的格式通常有2種:1、xml;2、json。通常來(lái)說(shuō)都是使用json來(lái)傳遞數(shù)據(jù)。本文正是介紹在java中json與對(duì)象之間互相轉(zhuǎn)換時(shí)遇到的幾個(gè)問(wèn)題以及相關(guān)的建議。

首先明確對(duì)于json有兩個(gè)概念:

json對(duì)象(javascript object notation,javascript對(duì)象表示法)。這看似只存是位javascript所定制的,但它作為一種語(yǔ)法是獨(dú)立于語(yǔ)言以及平臺(tái)的。只是說(shuō)通常情況下我們?cè)诳蛻?hù)端(瀏覽器)向服務(wù)器端傳遞數(shù)據(jù)時(shí),使用的是json格式,而這個(gè)格式是用于表示javascript對(duì)象。它是由一系列的“key-value”組成,如 {“id”: 1, “name”: “kevin”},這有點(diǎn)類(lèi)似map鍵值對(duì)的存儲(chǔ)方式。在java中所述的json對(duì)象,實(shí)際是指的jsonobject類(lèi),這在各個(gè)第三方的jsonjar包中通常都以這個(gè)名字命名,不同jar包對(duì)其內(nèi)部實(shí)現(xiàn)略有不同。

json字符串。json對(duì)象和json字符串之間的轉(zhuǎn)換是序列化與反序列化的過(guò)程,這就是好比java對(duì)象的序列化與反序列化。在網(wǎng)絡(luò)中數(shù)據(jù)的傳遞是通過(guò)字符串,或者是二進(jìn)制流等等進(jìn)行的,也就是說(shuō)在客戶(hù)端(瀏覽器)需要將數(shù)據(jù)以json格式傳遞時(shí),此時(shí)在網(wǎng)絡(luò)中傳遞的是字符串,而服務(wù)器端在接收到數(shù)據(jù)后當(dāng)然也是字符串(string類(lèi)型),有時(shí)就需要將json字符串轉(zhuǎn)換為json對(duì)象再做下一步操作(string類(lèi)型轉(zhuǎn)換為jsonobject類(lèi)型)。

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

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

1. 在java對(duì)象轉(zhuǎn)換json對(duì)象時(shí),get開(kāi)頭的所有方法會(huì)被轉(zhuǎn)換

這是什么意思呢,例如現(xiàn)有以下java對(duì)象。

?
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() {    //此類(lèi)中獲取sql語(yǔ)句的方法,并沒(méi)有對(duì)應(yīng)的屬性字段
    return "this is sql.";
  }
}

在我們將student對(duì)象轉(zhuǎn)換成json對(duì)象的時(shí)候,希望轉(zhuǎn)換后的json格式應(yīng)該是:

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

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

詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑

也就是說(shuō)可以猜測(cè)到的是,“net.sf.json”獲取java對(duì)象中public修飾符get開(kāi)頭的方法,并將其后綴定義為json對(duì)象的“key”,而將get開(kāi)頭方法的返回值定義為對(duì)應(yīng)key的“value”,注意是public修飾符get開(kāi)頭的方法,且有返回值。

我認(rèn)為這是不合理的轉(zhuǎn)換規(guī)則。如果我在java對(duì)象中定義了一個(gè)方法,僅僅因?yàn)檫@個(gè)方法是“get”開(kāi)頭,且有返回值就將其作為轉(zhuǎn)換后json對(duì)象的“key-value”,那豈不是暴露出來(lái)了?或者在返回給客戶(hù)端(瀏覽器)時(shí)候就直接暴露給了前端的console控制臺(tái)?作者規(guī)定了這種轉(zhuǎn)換規(guī)則,我想的大概原因是:既然你定義為了public方法,且命名為get,那就是有意將此方法暴露出來(lái)讓調(diào)用它的客戶(hù)端有權(quán)獲取。但我仍然認(rèn)為這不合理,甚至我定義它是一個(gè)bug。我這么定義也許也不合理,因?yàn)閾?jù)我實(shí)測(cè)發(fā)現(xiàn),不僅是“net.sf.json”包會(huì)按照這個(gè)規(guī)則進(jìn)行轉(zhuǎn)換,fastjson和jackson同樣也是照此規(guī)則,唯獨(dú)谷歌的gson并沒(méi)有按照這個(gè)規(guī)則進(jìn)行對(duì)象向json轉(zhuǎn)換。

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

詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑

其實(shí)propertydescriptor封裝得已經(jīng)很詳細(xì)了,什么讀寫(xiě)方法都已經(jīng)賦值了。

詳談Java中net.sf.json包關(guān)于JSON與對(duì)象互轉(zhuǎn)的坑

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

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

對(duì)net.sf.json的源碼簡(jiǎn)要分析了一下,發(fā)現(xiàn)確實(shí)如猜想的那樣,具體的源碼比較多篇幅有限需自行查看跟蹤。

2. 在json對(duì)象轉(zhuǎn)換java對(duì)象時(shí),list<long>會(huì)出現(xiàn)轉(zhuǎn)換錯(cuò)誤

標(biāo)題一句話解釋不清楚,這個(gè)問(wèn)題,我很確定地認(rèn)為它是一個(gè)bug。

現(xiàn)在有{"id": 1, "courseids": [1,2,3]}的json字符串,需要將它轉(zhuǎn)換為上文中提到的student對(duì)象,在student對(duì)象中有int和list<long>類(lèi)型的兩個(gè)屬性字段,也就是說(shuō)這個(gè)json字符串應(yīng)該轉(zhuǎn)換為對(duì)應(yīng)的數(shù)據(jù)類(lèi)型。

?
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);

 

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

?
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通過(guò)數(shù)字后的標(biāo)識(shí)符準(zhǔn)確地判斷了數(shù)據(jù)類(lèi)型,問(wèn)題出就出在獲得了這個(gè)值以及它的數(shù)據(jù)類(lèi)型后需要將它存入jsonobject中,而存入的過(guò)程中有jsonutils#transformnumber這個(gè)方法的存在,這個(gè)方法的存在,至少在目前看來(lái)純屬畫(huà)蛇添足。

?
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) {  //就算原類(lèi)型是long型,但是只要它在integer范圍,那么就最終還是會(huì)轉(zhuǎn)換為integer。
        return new integer(input.intvalue());
      }
    }
 
    return input;
  }
}

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

這兩個(gè)坑是偶然間發(fā)現(xiàn),建議還是不要使用早已沒(méi)有維護(hù)的net.sf.json的json包,另外有一點(diǎn),net.sf.json包對(duì)json格式的校驗(yàn)并不那么嚴(yán)格,如果這樣的格式“{"id": 1, "courseids": "[1,2,3]"}”,在其他三個(gè)包是會(huì)拋出異常的,但net.sf.json則不會(huì)。

以上這篇詳談java中net.sf.json包關(guān)于json與對(duì)象互轉(zhuǎn)的坑就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

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

延伸 · 閱讀

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

    10分鐘帶你了解DevOps工具

    上周,我的一些非常資深的同事和我本人評(píng)論了有多少新的DevOps工具正在涌現(xiàn),以及每天如何越來(lái)越難以追蹤它們以及它們?cè)谑澜缟系奈恢谩?..

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

    Java Swing樹(shù)狀組件JTree用法實(shí)例詳解

    這篇文章主要介紹了Java Swing樹(shù)狀組件JTree用法,結(jié)合具體實(shí)例形式分析了Swing組件JTree構(gòu)成樹(shù)狀列表的節(jié)點(diǎn)設(shè)置與事件響應(yīng),以及自定義圖形節(jié)點(diǎn)的相關(guān)操作技...

    SunnyYoona5542021-02-07
  • JAVA教程springboot DTO字符字段與日期字段的轉(zhuǎn)換問(wèn)題

    springboot DTO字符字段與日期字段的轉(zhuǎn)換問(wèn)題

    這篇文章主要介紹了springboot DTO字符字段與日期字段的轉(zhuǎn)換問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,...

    張占嶺3182020-07-18
  • JAVA教程SpringData關(guān)鍵字查詢(xún)實(shí)現(xiàn)方法詳解

    SpringData關(guān)鍵字查詢(xún)實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了SpringData關(guān)鍵字查詢(xún)實(shí)現(xiàn)方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以...

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

    Hibernate緩存詳解

    本文主要介紹了Hibernate緩存的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧 ...

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

    理解Java垃圾回收

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

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

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

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

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

    Spring MVC url提交參數(shù)和獲取參數(shù)

    本文重要講述通過(guò)url提交參數(shù)和獲取參數(shù)的具體操作與實(shí)現(xiàn)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧...

    Hoking4672020-09-07
主站蜘蛛池模板: luan小说 | 久久精品男人影院 | 高清国产在线观看 | 91se精品免费观看 | 边摸边吃奶又黄激烈视频韩国 | 草莓社区| 波多野给衣一区二区三区 | 久久re热在线视频精69 | 久久大胆视频 | 别停好爽好深好大好舒服视频 | 女医学护士一级毛片 | 久久全国免费观看视频 | 九九精品免视频国产成人 | 久久噜国产精品拍拍拍拍 | 免费看男女做好爽好硬视频 | 国内精品99 | 黄动漫软件车车好快的车车 | 青青青青青国产免费手机看视频 | chinese老太granny| 古装一级毛片 | 亚洲精品成人a | 精品国产在线观看 | 免费视频亚洲 | 欧美色青 | 99在线精品免费视频 | 久久永久影院免费 | 极品美女aⅴ高清在线观看 极品ts赵恩静和直男激战啪啪 | 草莓香蕉榴莲丝瓜秋葵绿巨人在线看 | 91混血大战上海双胞胎 | 亚洲高清中文字幕一区二区三区 | 男人晚上适合偷偷看的污污 | 国产一成人精品福利网站 | 亚洲白拍 | 四虎在线成人免费网站 | 国产欧美国产精品第一区 | 国产成人咱精品视频免费网站 | 精品日韩欧美一区二区三区 | 福利视频免费 | 国产精品成人一区二区1 | 全黄h全肉细节文在线观看 全彩成人18h漫画 | 翁熄性放纵交换300章 |