一、简介
1、Json简介
1.1.1 什么是json
JSON:(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript(欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。目前,Json处于数据交换语言的王者地位.
1.1.2 Json数组格式
Json的数据本身是数组,中括号包裹,数组的元素之间逗号分开.数组元素的数据类型没有限制.
var jsonArray = ["元素1","元素2","元素3"]; //定义数组格式json console.log(jsonArray[0]); //访问json数组的元素 for(var i = 0 ; i < jsonArray.length ; i++){ console.log(jsonArray[i]); //遍历数组,访问每个元素 }
1.1.3 Json对象格式
Json的数据本身是对象,大括号包裹.对象采用键值对形式存储,键固定为字符串类型,值是任意类型的数据.键和值使用冒号分开.
var jsonObject = {"k1":"v1","k2":"v2","k3":"v3"}; //定义对象格式json console.log(jsonObject.k1); //取出键k1对应的值
1.1.4 数组对象相互嵌套格式
- 数组中的元素是对象
var jsonArray = [ {"k1":"v1"},{"k2":"v2"} ]; // 定义数组格式json,数组元素是对象 console.log(jsonArray[0].k1); //访问数组0索引的元素,该元素的键k1对应的值
- 对象中的值是数组
var jsonObject = { "k1":["元素1","元素2"], "k2":["元素1","元素2"] }; // 定义对象格式json,键是字符串类型,值是数组 console.log(jsonObject.k1[0]); //访问对象的键是k1,对于的值为数组,数组的0索引元素
- 你中有我,我中有你
var json = { "k1":[ "元素1",{"key1":"value1"},{"key2":"value2"} ], "k2":[ {"key1":"value1"} ] }; //定义对象格式json,键是字符串,值是数组,数组的元素是对象 console.log(json.k1[1].key1); //访问json对象的键k1,对应的是数组,访问数组的1索引,数组的1索引上的元素是对象,访问key1键对应的值
2、FastJson简介
FastJson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。
Fastjson 的优点
- 速度快
fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越。 - 使用广泛
fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。 - 测试完备
fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个。每次发布都会进行回归测试,保证质量稳定。 - 使用简单
fastjson的 API 十分简洁。 - 功能完备
支持泛型,支持流处理超大文本,支持枚举,支持序列化和反序列化扩展。
二、序列化API
序列化 : 是指将Java对象转成json格式字符串的过程.JavaBean对象,List集合对象,Map集合,为应用最广泛的.Sy
JSON.toJSONString(students)
:可以支持对象,集合,map多种数据类型。
String jsonString = JSON.toJSONString(student);
序列化Java对象
Student student = new Student(); student.setName("张三"); student.setAge(20); String jsonString = JSON.toJSONString(student);
序列化集合(List)
Student student = new Student(); student.setEmail("zs@sina.com"); Student student1 = new Student(); student1.setId(1); ArrayList<Student> students = new ArrayList<>( students.add(student); students.add(student1); // 序列化 String s = JSON.toJSONString(students);
序列化Map
HashMap<String, String> map = new HashMap<>(); map.put("name","张三"); String s1 = JSON.toJSONString(map);
三、反序列化API
1、解析Java对象
// JSON类的静态方法 parseObject // 传递要反序列化的Json字符串,传递Java对象的Class对象 String objectString="{\"address\":\"北京市\",\"age\":20,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"}"; Student student = JSON.parseObject(objectString, Student.class);
2、解析List集合
// JSON类的静态方法,parseArray // 传递Json格式字符串,传递转换后的集合的泛型的Class对象 String listString="[{\"address\":\"北京市\",\"age\":20,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"},{\"address\":\"北京市\",\"age\":20,\"id\":1,\"name\":\"张三\"}]"; List<Student> students = JSON.parseArray(listString, Student.class);
3、解析Map集合
// JSON类的静态方法 parseObject // 传递要反序列化的Json字符串、要序列化的类型接口 String jsonString="{\"address\":\"北京市\",\"age\":20,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"}"; Map<String, String> map = JSON.parseObject(jsonString, new TypeReference<Map<String, String>>() {});
4、JSONObject()
JSONObject jsonObject = new JSONObject(); JSONObject user = jsonObject.getJSONObject("user"); JSONArray data = jsonObject.getJSONArray("data"); Student student = jsonObject.toJavaObject(Student.class);
5、JSONArray()
JSONArray jsonArray = new JSONArray(); JSONObject jsonObject1 = jsonArray.getJSONObject(0); JSONArray jsonArray1 = jsonArray.getJSONArray(0); List<Student> students = jsonArray.toJavaList(Student.class);
四、固定枚举注解
1、SerializerFeature枚举
该枚举支持序列化的一些特性数据定义。
名称 | 描述 |
QuoteFieldNames | 输出key时是否使用双引号,默认为true |
UseSingleQuotes | 使用单引号而不是双引号,默认为false |
WriteMapNullValue | 是否输出值为null的字段,默认为false |
WriteEnumUsingToString | Enum输出name()或者original,默认为false |
WriteEnumUsingName | 用枚举name()输出 |
UseISO8601DateFormat | Date使用ISO8601格式输出,默认为false |
WriteNullListAsEmpty | List字段如果为null,输出为[],而非null |
WriteNullStringAsEmpty | 字符类型字段如果为null,输出为”“,而非null |
WriteNullNumberAsZero | 数值字段如果为null,输出为0,而非null |
WriteNullBooleanAsFalse | Boolean字段如果为null,输出为false,而非null |
SkipTransientField | 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true |
SortField | 按字段名称排序后输出。默认为false |
(过期)WriteTabAsSpecial | 把\t做转义输出,默认为false |
PrettyFormat | 结果是否格式化,默认为false |
WriteClassName | 序列化时写入类型信息,默认为false。反序列化时需用到 |
DisableCircularReferenceDetect | 消除对同一对象循环引用的问题,默认为false |
WriteSlashAsSpecial | 对斜杠’/’进行转义 |
BrowserCompatible | 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false |
WriteDateUseDateFormat | 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat); |
(过期)DisableCheckSpecialChar | 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false |
4.1.1 WriteMapNullValue
// {"age":10,"id":0,"name":"张三"} String s = JSON.toJSONString(stu); // {"address":null,"age":10,"email":null,"id":null,"name":"张三"} String s = JSON.toJSONString(stu, SerializerFeature.WriteMapNullValue);
4.1.2 WriteNullNumberAsZero
// {"age":10,"id":0,"name":"张三"} String s = JSON.toJSONString(stu); // {"age":10,"id":0,"name":"张三"} String s = JSON.toJSONString(stu, SerializerFeature.WriteNullNumberAsZero);
4.1.3 WriteNullStringAsEmpty
// {"age":10,"id":0,"name":"张三"} String s = JSON.toJSONString(stu); // {"address":"","age":10,"email":"","name":"张三"} String s = JSON.toJSONString(stu, SerializerFeature.WriteNullStringAsEmpty);
4.1.4 WriteNullBooleanAsFalse
// {"age":10,"id":0,"name":"张三"} String s = JSON.toJSONString(stu); // {"age":10,"isBoolean":false,"name":"张三"} String s = JSON.toJSONString(stu, SerializerFeature.WriteNullBooleanAsFalse);
4.1.5 WriteDateUseDateFormat
{"age":10,"date":1652783554404,"name":"张三"} String s = JSON.toJSONString(stu); {"age":10,"date":"2022-05-17 18:31:59","name":"张三"} String s = JSON.toJSONString(stu, SerializerFeature.WriteDateUseDateFormat);
4.1.6 PrettyFormat
JSON.toJSONString(stu, SerializerFeature.WriteDateUseDateFormat,SerializerFeature.PrettyFormat);
{ "age":10, "date":"2022-05-17 18:37:11", "name":"张三" }
2、@JSonField注解
该注解作用于方法上,字段上和参数上.可在序列化和反序列化时进行特性功能定制.
- 注解属性 :
name
序列化后的名字 - 注解属性 :
ordinal
序列化后的顺序 - 注解属性 :
format
序列化后的格式 - 注解属性 :
serialize
是否序列化该字段 - 注解属性 :
deserialize
是否反序列化该字段 - 注解属性 :
serialzeFeatures
序列化时的特性定义(等同于SerializerFeature枚举) - 注解属性:
label
给属性打上标签, 相当于给属性进行了分组 - 注解属性:
serializeUsing
设置属性的序列化类 - 注解属性:
deserializeUsing
设置属性的反序列化类
@Data public class Student { // name 序列化后的名字 @JSONField(name = "myName") private String name; // ordinal序列化后的顺序 @JSONField(ordinal = 2) private Integer age; // ordinal序列化后的顺序 @JSONField(ordinal = 1) private String address; // serialize 是否序列化该字段 @JSONField(serialize = false) private String email; // deserialize 是否反序列化该字段 @JSONField(deserialize = false) private Boolean isBoolean; // 序列化时的特性定义 @JSONField(serialzeFeatures = {SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.PrettyFormat}) private String desc; // format 序列化后的格式 @JSONField(format = "YYYY-MM-dd") private Date date; }
3、@JSonType注解
@JSonType
与@JSONField
产生冲突时,@JSONField
优先级高。
该注解作用于类上,对该类的字段进行序列化和反序列化时的特性功能定制。
- 注解属性 : includes 要被序列化的字段。
- 注解属性 : orders 序列化后的顺序。
- 注解属性 : serialzeFeatures 序列化时的特性定义(等同于SerializerFeature枚举)。
@Data @JSONType( // includes 要被序列化的字段 includes = {"name", "age", "address"}, // orders 序列化后的顺序. orders = {"age", "name", "address"}, // serialzeFeatures 序列化时的特性定义 serialzeFeatures = {SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.PrettyFormat}) public class Student { private String name; private Integer age; // ordinal序列化后的顺序 @JSONField(ordinal = 1) private String address; }
五、Springboot集成FastJson做入参解析
- 配置的是SpringMVC的入参的格式。
- 假如没有注解配置,则走全局的配置。
- 假如有注解配置,以字段上的注解配置为准。
1、pom依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.68</version> </dependency>
2、配置类
5.2.1 配置代码
import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.nio.charset.StandardCharsets; /** * 配置FastJson解析器,默认是Jackson */ @Configuration public class HttpMessageConfig { @Bean public HttpMessageConverters fasJsonHttpMessageConverter() { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); // FastJsonConfig fastJsonConfig = getJsonCofig(); // fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); // fastJsonHttpMessageConverter通过封装FastjsonConfig配置全局 return new HttpMessageConverters(fastJsonHttpMessageConverter); } private FastJsonConfig getJsonCofig() { FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setCharset(StandardCharsets.UTF_8); // fastJsonConfig.setSerializerFeatures(); //序列化特性 // fastJsonConfig.setSerializeConfig(); //序列化配置-个性化 // fastJsonConfig.setParserConfig(); //反序列化配置 // fastJsonConfig.setSerializeFilters(); //序列化过滤器 return fastJsonConfig; } }
5.2.2 FastJsonConfig
public void setCharset(Charset charset); public void setSerializerFeatures(SerializerFeature... serializerFeatures); 序列化特性 public void setSerializeConfig(SerializeConfig serializeConfig); 序列化配置-个性化 public void setParserConfig(ParserConfig parserConfig); 反序列化配置 public void setSerializeFilters(SerializeFilter... serializeFilters); 序列化过滤器
5.2.3 SerializeConfig
// API public boolean put(Type type, ObjectSerializer value) serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase/PascalCase/...;
CamelCase策略,Java对象属性:personId,序列化后属性:persionId
PascalCase策略,Java对象属性:personId,序列化后属性:PersonId
SnakeCase策略,Java对象属性:personId,序列化后属性:person_id
KebabCase策略,Java对象属性:personId,序列化后属性:person-id
六、问题解决
1、$ref":"$[0]
重复引用
语法 | 描述 |
{“r e f " : " ref":"ref":"”} | 引用根对象 |
{“$ref”:“@”} | 引用自己 |
{“$ref”:“…”} | 引用父对象 |
{“$ref”:“…/…”} | 引用父对象的父对象 |
{“r e f " : " ref":"ref":".members[0].reportTo”} | 基于路径的引用 |
解决问题:
全局解决(配置类中):
fastJsonConfig.setSerializerFeatures( //去除重复引用 SerializerFeature.DisableCircularReferenceDetect )
如果能直接控制到序列化方法的话,可以
JSON.toJSONString(user,SerializerFeature.DisableCircularReferenceDetect);
测试:
SerializerFeature.DisableCircularReferenceDetect
:去除重复引用
Student student = new Student(); student.setId(1); student.setName("张三"); student.setAge(20); ArrayList<Student> students = new ArrayList<>(); students.add(student); students.add(student); System.out.println(JSON.toJSONString(students)); System.out.println(JSON.toJSONString(students, SerializerFeature.DisableCircularReferenceDetect));
结果:
[{"desc":"","id":1,"myName":"张三","age":20},{"$ref":"$[0]"}] =========== [{"desc":"","id":1,"myName":"张三","age":20},{"desc":"","id":1,"myName":"张三","age":20}]
2、BigDecimal类型设置
方案一:使用SerializeFilter处理
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); SerializeConfig serializeConfig = new SerializeConfig(); fastJsonConfig.setSerializeConfig(serializeConfig); PropertyFilter propertyFilter = new PropertyFilter() { @Override public boolean apply(Object object, String name, Object value) { if(value instanceof BigDecimal){ return false; } return true; } }; AfterFilter afterFilter = new AfterFilter() { @Override public void writeAfter(Object object) { Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { if (field.getType() == BigDecimal.class) { field.setAccessible(true); Object value= null; try { value = (BigDecimal)field.get(object); value = ((BigDecimal) value).setScale(2,BigDecimal.ROUND_DOWN); } catch (IllegalAccessException e) { e.printStackTrace(); } writeKeyValue(field.getName(), value ); } } } }; fastJsonConfig.setSerializeFilters(propertyFilter,afterFilter); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
方案二:
ValueFilter valueFilter = new ValueFilter() { @Override public Object process(Object object, String name, Object value) { if(value instanceof BigDecimal){ value = ((BigDecimal)value).setScale(3,BigDecimal.ROUND_DOWN); } return value; } }; fastJsonConfig.setSerializeFilters(valueFilter);