前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java中net.sf.json包关于JSON与对象互转的坑

Java中net.sf.json包关于JSON与对象互转的坑

作者头像
用户1148394
发布于 2018-01-09 10:16:20
发布于 2018-01-09 10:16:20
3.5K00
代码可运行
举报
文章被收录于专栏:余林丰余林丰
运行总次数:0
代码可运行

  在Web开发过程中离不开数据的交互,这就需要规定交互数据的相关格式,以便数据在客户端与服务器之间进行传递。数据的格式通常有2种:1、xml;2、JSON。通常来说都是使用JSON来传递数据。本文正是介绍在Java中JSON与对象之间互相转换时遇到的几个问题以及相关的建议。 首先明确对于JSON有两个概念:

  1. JSON对象(JavaScript Object Notation,JavaScript对象表示法)。这看似只存是位JavaScript所定制的,但它作为一种语法是独立于语言以及平台的。只是说通常情况下我们在客户端(浏览器)向服务器端传递数据时,使用的是JSON格式,而这个格式是用于表示JavaScript对象。它是由一系列的“key-value”组成,如 {“id”: 1, “name”: “kevin”},这有点类似Map键值对的存储方式。在Java中所述的JSON对象,实际是指的JSONObject类,这在各个第三方的JSONjar包中通常都以这个名字命名,不同jar包对其内部实现略有不同。
  2. 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对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 package sfjson;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * Created by Kevin on 2017/12/1.
 7  */
 8 public class Student {
 9     private int id;
10     private List<Long> courseIds;
11 
12     public int getId() {
13         return id;
14     }
15 
16     public void setId(int id) {
17         this.id = id;
18     }
19 
20     public List<Long> getCourseIds() {
21         return courseIds;
22     }
23 
24     public void setCourseIds(List<Long> courseIds) {
25         this.courseIds = courseIds;
26     }
27 
28     public String getSql() {        //此类中获取sql语句的方法,并没有对应的属性字段
29         return "this is sql.";
30     }
31 }

  在我们将Student对象转换成JSON对象的时候,希望转换后的JSON格式应该是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 {
2     "id": 1,
3     "courseIds": [1, 2, 3]
4 }

  然而在使用“net.sf.json”包的JSONObject json = JSONObject.fromObject(student); API转换后的结果却是:

image.png
image.png

  也就是说可以猜测到的是,“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。

  其实PropertyDescriptor封装得已经很详细了,什么读写方法都已经赋值了。

  例如这个getSql方法已经被解析成了上图的PropertyDescriptor。之后的通过这个类将一些方法过滤掉,例如getClass方法不是POJO中的方法,所以并不需要将它转换成JSON对象。而PropertyDescriptor的获取是通过BeanInfo#getPropertyDescriptors,而BeanInfo的获取则又是通过new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();不断深入最后就会到达如下方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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字符串应该转换为对应的数据类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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型,从这里来看最后的结果应该是对的啊。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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这个方法的存在,这个方法的存在,至少在目前看来纯属画蛇添足。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 public static Number transformNumber(Number input) {
 2     if (input instanceof Float) {
 3         return new Double(input.toString());
 4     } else if (input instanceof Short) {
 5         return new Integer(input.intValue());
 6     } else if (input instanceof Byte) {
 7         return new Integer(input.intValue());
 8     } else {
 9         if (input instanceof Long) {
10             Long max = new Long(2147483647L);
11             if (input.longValue() <= max.longValue() && input.longValue() >= -2147483648L) {    //就算原类型是Long型,但是只要它在Integer范围,那么就最终还是会转换为Integer。
12                 return new Integer(input.intValue());
13             }
14         }
15 
16         return input;
17     }
18 }

  上面的这段代码很清晰的显示了元凶所在,不论是Long型(Integer范围内的Long型),包括Byte、Short都会转换为Integer。尚不明白这段代码的意义在哪里。前面又要根据数字后的字母确定准确的数据类型,后面又要将准确的数据类型转换一次,这就导致了开头提到的那个bug。这个问题几乎是无法回避,所以最好的办法就是不要用。

  这两个坑是偶然间发现,建议还是不要使用早已没有维护的net.sf.json的JSON包,另外有一点,net.sf.json包对JSON格式的校验并不那么严格,如果这样的格式“{"id": 1, "courseIds": "[1,2,3]"}”,在其他三个包是会抛出异常的,但net.sf.json则不会。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-12-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
java对象与json对象间的相互转换
JSONObject jsonObject = JSONObject.fromObject(jsonStr);
ZhangXianSheng
2019/05/28
2.6K0
JSON-lib框架,转换JSON、XML不再困难
Json-lib可以将Java对象转成json格式的字符串,也可以将Java对象转换成xml格式的文档,同样可以将json字符串转换成Java对象或是将xml字符串转换成Java对象。 一、 准备工作 1、 首先要去官方下载json-lib工具包 下载地址: http://sourceforge.net/projects/json-lib/files/json-lib/json-lib-2.4/ 目前最新的是2.4的版本,本示例中使用的是v2.3;json-lib还需要以下依赖包: jakarta com
cloudskyme
2018/03/20
3.3K0
JSON-lib框架,转换JSON、XML不再困难
net.sf.json.JSONException: null object_json数组转json对象
在程序开发过程中,在参数传递,函数返回值等方面,越来越多的使用JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,同时也易于机器解析和生成、易于理解、阅读和撰写,而且Json采用完全独立于语言的文本格式,这使得Json成为理想的数据交换语言。 JSON建构于两种结构:
全栈程序员站长
2022/11/08
6.1K0
net.sf.json.JSONException: null object_json数组转json对象
java list 转json 字符串_JSON的String字符串与Java的List列表对象的相互转换
2.如果是需要把前端的List对象转换为json传到后台,param是ajax的参数,那么转换如下所示:
全栈程序员站长
2022/11/02
9.4K0
【JSON解析】浅谈JSONObject的使用[通俗易懂]
在程序开发过程中,在参数传递,函数返回值等方面,越来越多的使用JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,同时也易于机器解析和生成、易于理解、阅读和撰写,而且Json采用完全独立于语言的文本格式,这使得Json成为理想的数据交换语言。 JSON建构于两种结构:
全栈程序员站长
2022/09/18
5.3K0
【JSON解析】浅谈JSONObject的使用[通俗易懂]
java json对象和json字符串互转的方法_java json转字符串
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/08
2.4K0
Json对象与Json字符串互转(4种转换方式)
  $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象
李才哥
2019/07/10
14.9K0
Json对象与Json字符串互转(4种转换方式)
Java对象,Map,List,Set数组等相互转换大全(详细讲解,附代码,讲解案例)
本人记录,因为基础比较差整理出来方便自己学习; 同样也希望可以对大家有帮助,有可以优化的地方的意见和建议,欢迎提出,鄙人虚心接受,认真改正;
默 语
2024/11/20
2330
Java对象,Map,List,Set数组等相互转换大全(详细讲解,附代码,讲解案例)
Java 处理json经常使用代码
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/115940.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/08
4670
java字符串转换为json对象6_Json对象与Json字符串的转化、JSON字符串与Java对象的转换…
$.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象
全栈程序员站长
2022/11/08
2.6K0
Json详解以及fastjson使用教程[通俗易懂]
Json是一种轻量级的数据交换格式,采用一种“键:值”对的文本格式来存储和表示数据,在系统交换数据过程中常常被使用,是一种理想的数据交换语言。在使用Java做Web开发时,不可避免的会遇到Json的使用。下面我们就简单讲一下Json的使用以及fastjson.jar包的使用。
全栈程序员站长
2022/07/25
2.6K0
Json详解以及fastjson使用教程[通俗易懂]
JSONObject.fromObject – JSON与对象的转换
JSON与JAVA数据的转换( JSON即JavaScript对象游泳,要么就,它是一种轻量级的数据交换格式,非常适合于服务器与JavaScript的的交互。)
全栈程序员站长
2022/06/27
6260
java json对象和json字符串互转的方法_js中对象转字符串的方法
JSON字符串与JavaBean之间的转换建议使用TypeReference<T>类
全栈程序员站长
2022/09/27
4.8K0
java对象转map_java中实现map与对象相互转换的几种实现
public static Object mapToObject(Map map, Class> beanClass)
全栈程序员站长
2022/08/18
2.1K0
JSON 中JsonConfig的使用问题
在前后端数据传输交互中,经常会遇到字符串(String)与json,XML等格式相互转换与解析,其中json以跨语言,跨前后端的优点在开发中被频繁使用,基本上可以说是标准的数据交换格式。以前用fastjson比较多,最近项目使用net.sf.json包进行json格式转换,也碰到一些问题在这里记录一下。
程序你好
2022/11/18
1.8K0
JSON 中JsonConfig的使用问题
初识json
JSON: JavaScript Object Notation,是一种数据格式,对,仅仅是一种格式。 和它类似的还有XML;
Mshu
2018/10/31
4530
开发:随笔记录之 Json字符串和对象的相互转换
import net.sf.json.JSONArray; import net.sf.json.JSONObject;
执笔记忆的空白
2020/12/25
3440
json字符串转对象的几种方式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/08
4.4K0
java json对象和json字符串互转的方法_将json字符串转换成对象
这个json字符串中包含的数据可以转化为一个List<People>集合,代码如下:
全栈程序员站长
2022/11/09
2.1K0
json与类,集合的转换(四)之Json-lib实现
周杰伦本人
2023/10/12
2610
json与类,集合的转换(四)之Json-lib实现
推荐阅读
相关推荐
java对象与json对象间的相互转换
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验