Java使用Fastjson进行JSON数据操作教程详解
1. 工具简介
Fastjson 是一个 Java 库,可以用来将 Java 对象转换为它们的 JSON 表示。它还可以用于将 JSON 字符串转换为等价的 Java 对象。Fastjson 可以处理任意 Java 对象,包括没有源代码的已存在对象。
Fastjson 目标:
- 在服务器端和 android 客户端上提供最佳性能。
- 提供简单的
toJSONString()
和parseObject()
方法将 Java 对象转换为 JSON,反之亦然。 - 允许预先存在的不可修改对象与 JSON 之间的转换。
- 广泛支持 Java 泛型。
- 允许对象的自定义表示。
- 支持任意复杂的对象(具有深度继承层次结构和广泛使用泛型类型)。
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.72.android</version> </dependency>
Fastjson 是阿里巴巴开源的 JSON 解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。fastjson 已经被广泛使用在各种场景,包括 cache 存储、RPC 通讯、MQ 通讯、网络协议通讯、Android 客户端、Ajax 服务器处理程序等等。
Fastjson 的 API 十分简洁:
String text = JSON.toJSONString(obj); //序列化 VO vo = JSON.parseObject("{...}", VO.class); //反序列化
2. JSON 对象使用
com.alibaba.fastjson.JSON
这个类是 fastjson API 的入口,主要的功能都通过这个类提供。
2.1. String 转 JSON
对于字符串的处理,主要是看这个字符串(jsonStr)是 JSON 对象格式还是 JSON 数组格式,然后选择对应的方法处理就行。
JSON 对象字符串转为 JSON 对象:
JSONObject jsonObj = JSON.parseObject(jsonStr);
JSON 数组字符串转为 JSON 数组对象:
JSONArray jsonArr = JSON.parseArray(jsonStr);
2.2. String 转 JavaBean
Model model = JSON.parseObject(jsonStr, Model.class);
2.3. Object 转 String
包括 JSONObject、JSONArray、JavaBean、数组、List、Set、Map 都可以通过这种方式转 String。
String jsonStr = JSON.toJSONString(object);
3. @JSONField 注解使用
这是一个注解,用于配置在 JavaBean,可以配置在 getter/setter 方法或者字段上,也可以直接配置在属性上。
注意:若属性是私有的,必须有 set* 方法。否则无法反序列化。
部分属性示例:
//配置序列化的字段顺序(1.1.42版本之后才支持) @JSONField(ordinal=1) //是否参与序列化:该字段不输出,但是如果加了final,这个字段就无法被过滤 @JSONField(serialize=false) //是否参与反序列化:该字段不输出,但是如果加了final,这个字段就无法被过滤 @JSONField(derialize=false) //日期按照指定格式序列化 @JSONField(format="yyyy-MM-dd HH:mm:ss") //使用字段别名 @JSONField(name="别名") //序列化规则 @JSONField(serialzeFeatures={SerialzeFeatures属性}) //反序列化规则 @JSONField(parseFeatures={Features属性});
3.1. 序列化 SerializerFeature 属性
public enum SerializerFeature { /** * 输出key时是否使用双引号,默认为true */ QuoteFieldNames, /** * 使用单引号而不是双引号,默认为false */ UseSingleQuotes, /** * 是否输出值为null的字段,默认为false */ WriteMapNullValue, /** * 用枚举toString()值输出 */ WriteEnumUsingToString, /** * 用枚举name()输出 */ WriteEnumUsingName, /** * Date使用ISO8601格式输出,默认为false */ UseISO8601DateFormat, /** * @since 1.1 * List字段如果为null,输出为[],而非null */ WriteNullListAsEmpty, /** * @since 1.1 * 字符类型字段如果为null,输出为"",而非null */ WriteNullStringAsEmpty, /** * @since 1.1 * 数值字段如果为null,输出为0,而非null */ WriteNullNumberAsZero, /** * @since 1.1 * Boolean字段如果为null,输出为false,而非null */ WriteNullBooleanAsFalse, /** * @since 1.1 * 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true */ SkipTransientField, /** * @since 1.1 * 按字段名称排序后输出。默认为false */ SortField, /** * @since 1.1.1 * 把\t做转义输出,默认为false(不推荐,已删除) */ @Deprecated WriteTabAsSpecial, /** * @since 1.1.2 * 结果是否格式化,默认为false */ PrettyFormat, /** * @since 1.1.2 * 序列化时写入类型信息,默认为false。反序列化时需用到 */ WriteClassName, /** * @since 1.1.6 * 消除对同一对象循环引用的问题,默认为false */ DisableCircularReferenceDetect, /** * @since 1.1.9 * 对斜杠"/"进行转义 */ WriteSlashAsSpecial, /** * @since 1.1.10 * 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false */ BrowserCompatible, /** * @since 1.1.14 * 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat); */ WriteDateUseDateFormat, /** * @since 1.1.15 */ NotWriteRootClassName, /** * @since 1.1.19 * 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false */ DisableCheckSpecialChar, /** * @since 1.1.35 * 将对象转为array输出 */ BeanToArray, /** * @since 1.1.37 */ WriteNonStringKeyAsString, /** * @since 1.1.42 */ NotWriteDefaultValue, /** * @since 1.2.6 */ BrowserSecure, /** * @since 1.2.7 */ IgnoreNonFieldGetter, /** * @since 1.2.9 */ WriteNonStringValueAsString, /** * @since 1.2.11 */ IgnoreErrorGetter; }
3.2. 反序列化 Feature 属性
public enum Feature { /** * 这个特性,决定了解析器是否将自动关闭那些不属于parser自己的输入源。 * 如果禁止,则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader; * 如果允许,parser只要自己需要获取closed方法(当遇到输入流结束,或者parser自己调用 JsonParder#close方法),就会处理流关闭。 * 注意:这个属性默认是true,即允许自动关闭流 */ AutoCloseSource, /** * 该特性决定parser将是否允许解析使用Java/C++ 样式的注释(包括'/'+'*' 和'//' 变量)。 * 由于JSON标准说明书上面没有提到注释是否是合法的组成,所以这是一个非标准的特性;尽管如此,这个特性还是被广泛地使用。 * 注意:该属性默认是false,因此必须显式允许,即通过JsonParser.Feature.ALLOW_COMMENTS 配置为true。 */ AllowComment, /** * 这个特性决定parser是否将允许使用非双引号属性名字, (这种形式在Javascript中被允许,但是JSON标准说明书中没有)。 * 注意:由于JSON标准上需要为属性名称使用双引号,所以这也是一个非标准特性,默认是false的。 * 同样,需要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true,打开该特性。 */ AllowUnQuotedFieldNames, /** * 该特性决定parser是否允许单引号来包住属性名称和字符串值。 * 注意:默认下,该属性也是关闭的。需要设置JsonParser.Feature.ALLOW_SINGLE_QUOTES为true */ AllowSingleQuotes, /** * 该特性决定JSON对象属性名称是否可以被String#intern 规范化表示。如果允许,则JSON所有的属性名将会 intern() ; * 如果不设置,则不会规范化,默认下,该属性是开放的。此外,必须设置CANONICALIZE_FIELD_NAMES为true * 关于intern方法作用:当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串 (该对象由 equals(Object) 方法确定),则返回池中的字符串。 * 否则,将此 String 对象添加到池中, 并且返回此 String 对象的引用。 */ InternFieldNames, /** * 这个设置为true则遇到字符串符合ISO8601格式的日期时,会直接转换成日期类。 */ AllowISO8601DateFormat, /** * 允许多重逗号,如果设为true,则遇到多个逗号会直接跳过。 * {"a":1,,,"b":2} */ AllowArbitraryCommas, /** * 这个设置为true则用BigDecimal类来装载数字,否则用的是double; */ UseBigDecimal, /** * @since 1.1.2 * 忽略不匹配 */ IgnoreNotMatch, /** * @since 1.1.3 * 如果你用fastjson序列化的文本,输出的结果是按照fieldName排序输出的,parser时也能利用这个顺序进行优化读取。这种情况下,parser能够获得非常好的性能 */ SortFeidFastMatch, /** * @since 1.1.3 * 禁用ASM */ DisableASM, /** * @since 1.1.7 * 禁用循环引用检测 */ DisableCircularReferenceDetect, /** * @since 1.1.10 * 对于没有值的字符串属性设置为空串 */ InitStringFieldAsEmpty, /** * @since 1.1.35 * 支持数组to对象 */ SupportArrayToBean, /** * @since 1.2.3 * 属性保持原来的顺序 */ OrderedField, /** * @since 1.2.5 * 禁用特殊字符检查 */ DisableSpecialKeyDetect, /** * @since 1.2.9 * 使用对象数组 */ UseObjectArray; }
3.3. 测试代码
JavaBean:
class User { //指定序列化字段顺序,字段名称 @JSONField(ordinal=4,name="ID") private Integer id; //指定序列化字段顺序,不参加序列化 @JSONField(ordinal=3,serialize=false) private String name; //指定序列化字段顺序,不参加反序列化 @JSONField(ordinal=2,deserialize=false) private Integer age; //指定序列化字段顺序,日期格式 @JSONField(ordinal=1,format="yyyy-MM-dd") private Date creattime; //指定序列化规则,字段为null的时候依然参加序列化 @JSONField(serialzeFeatures=SerializerFeature.WriteMapNullValue) private String phone; public Integer getId() {return id;} public Date getCreattime() {return creattime;} public void setId(Integer id) {this.id = id;} public void setCreattime(Date creattime) {this.creattime = creattime;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Integer getAge() {return age;} public void setAge(Integer age) {this.age = age;} public String getPhone() {return phone;} public void setPhone(String phone) {this.phone = phone;} @Override public String toString() { return "id="+id+"; name="+name+"; age="+age+"; createtime="+creattime; } };
测试代码:
User user = new User(); user.setId(123456); user.setName("wangbo"); user.setAge(28); user.setCreattime(new Date()); String userStr = JSON.toJSONString(user); System.out.println(userStr); User user2 = JSON.parseObject(userStr, User.class); System.out.println(user2);
执行结果:
userStr
{"phone":null,"creattime":"2018-12-04","age":28,"ID":123456}
user2
id=123456; name=null; age=null; createtime=Tue Dec 04 00:00:00 CST 2018
可以看出:
第一步序列化的结果:按照指定字段顺序序列化的,id 字段序列化为 ID,name 没有参加序列化,createtime 按照指定格式序列化,phone 为 null,但是参与了序列化。
FastJson 默认的序列化规则是字段的值为 null 的时候,不参与序列化,serialzeFeatures=SerializerFeature.WriteMapNullValue
可以在 value 的值为null 的时候,依然会把它的值序列化出来。
第二部反序列化结果:age 没有参与反序列化。
4. JSONPath 对象使用
FastJson 1.2.0 之后的版本支持 JSONPath。这是一个很强大的功能,可以在Java 框架中当作对象查询语言(OQL)来使用。
4.1. API 方法
public class JSONPath { //求值,静态方法 public static Object eval(Object rootObject, String path); //计算size,Map非空元素个数,对象非空元素个数,Collection的Size,数组的长度。其他无法求值返回-1 public static int size(Object rootObject, String path); //是否包含,path中是否存在对象 public static boolean contains(Object rootObject, String path); //是否包含,path中是否存在指定值,如果是集合或者数组,在集合中查找value是否存在 public static boolean containsValue(Object rootObject, String path, Object value); //在数组或者集合中添加元素 public static void arrayAdd(Object rootObject, String path, Object... values); //修改制定路径的值,如果修改成功,返回true,否则返回false public static boolean set(Object rootObject, String path, Object value); }
4.2. 语法
JSONPATH | 描述 |
---|---|
$ | 根对象,例如$.name |
[num] | 数组访问,其中num是数字,可以是负数。例如$[0].leader.departments[-1].name |
[num0,num1,num2...] | 数组多个元素访问,其中num是数字,可以是负数,返回数组中的多个元素。例如$[0,3,-2,5] |
[start:end] | 数组范围访问,其中start和end是开始小表和结束下标,可以是负数,返回数组中的多个元素。例如$[0:5] |
[start:end :step] | 数组范围访问,其中start和end是开始小表和结束下标,可以是负数;step是步长,返回数组中的多个元素。例如$[0:5:2] |
[?(key)] | 对象属性非空过滤,例如$.departs[?(name)] |
[key > 123] | 数值类型对象属性比较过滤,例如$.departs[id >= 123],比较操作符支持=,!=,>,>=,<,<= |
[key = '123'] | 字符串类型对象属性比较过滤,例如$.departs[name = '123'],比较操作符支持=,!=,>,>=,<,<= |
[key like 'aa%'] | 字符串类型like过滤,例如$.departs[name like 'sz*'],通配符只支持%,支持not like |
[key rlike 'regexpr'] | 字符串类型正则匹配过滤,例如departs[name like 'aa(.)*'],正则语法为jdk的正则语法,支持not rlike |
[key in ('v0', 'v1')] | IN过滤, 支持字符串和数值类型。例如: $.departs[name in ('wenshao','Yako')]$.departs[id not in (101,102)] |
[key between 234 and 456] | BETWEEN过滤, 支持数值类型,支持not between。例如: $.departs[id between 101 and 201]$.departs[id not between 101 and 201] |
length() 或者 size() | 数组长度。例如$.values.size()。支持类型java.util.Map、java.util.Collection和数组 |
. | 属性访问,例如$.name |
.. | deepScan属性访问,例如$..name |
* | 对象的所有属性,例如$.leader.* |
['key'] | 属性访问。例如$['name'] |
['key0','key1'] | 多个属性访问。例如$['id','name'] |
注意:以下两种写法的语义是相同的:
$.store.book[0].title $['store']['book'][0]['title']
语法示例
JSONPath | 语义 |
---|---|
$ | 根对象 |
$[-1] | 最后元素 |
$[:-2] | 第1个至倒数第2个 |
$[1:] | 第2个之后所有元素 |
$[1,2,3] | 集合中1,2,3个元素 |
4.3. 代码示例
package com.wangbo.fastjson; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; public class Test { public static void main(String[] args) { String jsonStr = "{\n" + " \"store\": {\n" + " \"bicycle\": {\n" + " \"color\": \"red\",\n" + " \"price\": 19.95\n" + " },\n" + " \"book\": [\n" + " {\n" + " \"author\": \"刘慈欣\",\n" + " \"price\": 8.95,\n" + " \"category\": \"科幻\",\n" + " \"title\": \"三体\"\n" + " },\n" + " {\n" + " \"author\": \"itguang\",\n" + " \"price\": 12.99,\n" + " \"category\": \"编程语言\",\n" + " \"title\": \"go语言实战\"\n" + " }\n" + " ]\n" + " }\n" + "}"; JSONObject jsonObject = JSON.parseObject(jsonStr); System.out.println(jsonObject.toString()); //得到所有的书 List<Book> books = (List<Book>) JSONPath.eval(jsonObject, "$.store.book"); System.out.println("books=" + books); //得到所有的书名 List<String> titles = (List<String>) JSONPath.eval(jsonObject, "$.store.book.title"); System.out.println("titles=" + titles); //第一本书title String title = (String) JSONPath.read(jsonStr, "$.store.book[0].title"); System.out.println("title=" + title); //price大于10元的book List<Book> list = (List<Book>) JSONPath.read(jsonStr, "$.store.book[price > 10]"); System.out.println("price大于10元的book="+ list); //price大于10元的title List<String> list2 =(List<String>) JSONPath.read(jsonStr, "$.store.book[price > 10].title"); System.out.println("price大于10元的title=" + list2); //category(类别)为科幻的book List<Book> list3 = (List<Book>) JSONPath.read(jsonStr,"$.store.book[category = '科幻']"); System.out.println("category(类别)为科幻的book=" + list3); //bicycle的所有属性值 Collection<String> values = (Collection<String>) JSONPath.eval(jsonObject, "$.store.bicycle.*"); System.out.println("bicycle的所有属性值={}" + values); //bicycle的color和price属性值 List<String> read =(List<String>) JSONPath.read(jsonStr, "$.store.bicycle['color','price']"); System.out.println("bicycle的color和price属性值=" + read); } }
运行结果
{"store":{"bicycle":{"color":"red","price":19.95},"book":[{"author":"刘慈欣","price":8.95,"category":"科幻","title":"三体"},{"author":"itguang","price":12.99,"category":"编程语言","title":"go语言实战"}]}}
books=[{"author":"刘慈欣","price":8.95,"category":"科幻","title":"三体"},{"author":"itguang","price":12.99,"category":"编程语言","title":"go语言实战"}]
titles=["三体","go语言实战"]
title=三体
price大于10元的book=[{"author":"itguang","price":12.99,"category":"编程语言","title":"go语言实战"}]
price大于10元的title=["go语言实战"]
category(类别)为科幻的book=[{"author":"刘慈欣","price":8.95,"category":"科幻","title":"三体"}]
bicycle的所有属性值={}[red, 19.95]
bicycle的color和price属性值=[red, 19.95]
5. 自定义序列化规则
package com.gtcom.web.json; import com.alibaba.fastjson.serializer.ValueFilter; import org.apache.commons.lang3.ObjectUtils; /** * 空列表序列化 * * @author wangbo * @date 2021/11/9 */ public class EmptyListFilter implements ValueFilter { @Override public Object process(Object object, String name, Object value) { if (ObjectUtils.isEmpty(value)) { return null; } return value; } }
空 List 不会被序列化:
private final static ValueFilter VALUE_FILTER = new EmptyListFilter(); User user = new User(); JSON.toJSONString(user, VALUE_FILTER);
如果 User 对象中有一个 List 字段,则:
- 未使用该自定义序列化规则:List 字段为 null,序列化结果不包含该字段;List 字段为空,序列化结果包含该字段。
- 使用了该自定义序列化规则:List 字段为 null,序列化结果不包含该字段;List 字段为空,序列化结果不包含该字段。
6. 遇到的问题
遇到一个问题,方法参数传过来是一个Object
,其实是个JSON
格式的字符串,我在使用JSON.toJSONString(object)
方法转化为String
,然后通过JSON.parseObject(string)
方法转化为JSONObject
的时候报错了,提示can not cast to JSONObject.
错误。
测试代码:
package com.gtcom.governance.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; /** * @author wangbo * @date 2021/6/23 */ public class TestJson { public static void main(String[] args) { Object string = "{\"name\":\"zhangsan\"}"; System.out.println(string); System.out.println("========"); JSONObject jsonObject1 = JSON.parseObject(string.toString()); System.out.println(jsonObject1); System.out.println("========"); String jsonStr = JSON.toJSONString(string); System.out.println(jsonStr); System.out.println("========"); JSONObject jsonObject2 = JSON.parseObject(jsonStr); System.out.println(jsonObject2); } }
控制台输出:
{"name":"zhangsan"}
========
{"name":"zhangsan"}
========
"{\"name\":\"zhangsan\"}"
========
Exception in thread "main" com.alibaba.fastjson.JSONException: can not cast to JSONObject.
at com.alibaba.fastjson.JSON.parseObject(JSON.java:262)
at com.gtcom.governance.utils.TestJson.main(TestJson.java:25)
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class com.alibaba.fastjson.JSONObject (java.lang.String is in module java.base of loader 'bootstrap'; com.alibaba.fastjson.JSONObject is in unnamed module of loader 'app')
at com.alibaba.fastjson.JSON.parseObject(JSON.java:260)
... 1 more
结论:
JSON.toJSONString(object)
方法将转义符也写入字符串了,再进行JSONObject
转化就会报异常。所以对于String
类型的Object
直接使用toString
方法就行。
以上就是Java使用Fastjson进行JSON数据操作教程详解的详细内容,更多关于Java Fastjson操作JSON的资料请关注脚本之家其它相关文章!
最新评论