Java 与JSON
在工作中有很多的文件格式都是采用Json格式传输数据。当我们接受到Json格式的数据需要把json数据转成对象进行数据处理或者是把数据转换json格式进行数据输出。Java把对象转换json,或者把json转换成对象都有哪些工具库呢??
json介绍
JSON(Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。
JSON是一个序列化的对象或数组。
JSON 的语法规则十分简单,可称得上“优雅完美”,总结起来有
- 数组(Array)用方括号(“[]”)表示。
- 对象(0bject)用大括号(“{}”)表示。
- 名称/值对(name/value)组合成数组和对象。
- 名称(name)置于双引号中,值(value)有字符串、数值、布尔值、null、对象和数组。
- 并列的数据之间用逗号(“,”)分隔
{
"name": "xdr630",
"favorite": "programming"
}
json类库
json不管是在Web开发还是服务器开发中是相当常见的数据传输格式,一般情况我们对于JSON解析构造的性能并不需要过于关心,除非是在性能要求比较高的系统。
目前对于Java开源的JSON类库有很多种,下面我们取4个常用的JSON库进行性能测试对比, 同时根据测试结果分析如果根据实际应用场景选择最合适的JSON库。
这4个JSON类库分别为:Gson,FastJson,Jackson,Json-lib。
选择一个合适的JSON库要从多个方面进行考虑:
- 字符串解析成JSON性能
- 字符串解析成JavaBean性能
- JavaBean构造JSON性能
- 集合构造JSON性能
- 易用性
先简单介绍下四个类库的身份背景
Gson
项目地址:https://github.com/google/gson
Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。 Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。 在使用这种对象转换之前,需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。 类里面只要有get和set方法,Gson完全可以实现复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
FastJson
项目地址:https://github.com/alibaba/fastjson
Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。 FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。 FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。
Jackson
项目地址:https://github.com/FasterXML/jackson
Jackson是当前用的比较广泛的,用来序列化和反序列化json的Java开源框架。Jackson社区相对比较活跃,更新速度也比较快, 从Github中的统计来看,Jackson是最流行的json解析器之一,Spring MVC的默认json解析器便是Jackson。
Jackson优点很多:
- Jackson 所依赖的jar包较少,简单易用。
- 与其他 Java 的 json 的框架 Gson 等相比,Jackson 解析大的 json 文件速度比较快。
- Jackson 运行时占用内存比较低,性能比较好
- Jackson 有灵活的 API,可以很容易进行扩展和定制。
目前最新版本是2.9.4,Jackson 的核心模块由三部分组成:
- jackson-core 核心包,提供基于”流模式”解析的相关 API,它包括 JsonPaser 和 JsonGenerator。Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
- jackson-annotations 注解包,提供标准注解功能;
- jackson-databind 数据绑定包,提供基于”对象绑定” 解析的相关 API( ObjectMapper )和”树模型” 解析的相关 API(JsonNode);基于”对象绑定” 解析的 API 和”树模型”解析的 API 依赖基于”流模式”解析的 API。
Json-lib
项目地址:http://json-lib.sourceforge.net/index.html
json-lib最开始的也是应用最广泛的json解析工具,json-lib 不好的地方确实是依赖于很多第三方包,对于复杂类型的转换,json-lib对于json转换成bean还有缺陷, 比如一个类里面会出现另一个类的list或者map集合,json-lib从json到bean的转换就会出现问题。json-lib在功能和性能上面都不能满足现在互联网化的需求。
比测试
1、pom依赖
<!-- Json libs-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.14</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.4</version>
</dependency>
测试比较方法
https://blog.csdn.net/lin443514407lin/article/details/85262479
Fastjson
Fastjson 是一个 Java 库,可用于将 Java 对象转换为其 JSON 表示。它还可用于将 JSON 字符串转换为等效的 Java 对象。Fastjson 可以处理任意 Java 对象,包括您没有源代码的预先存在的对象。
Json是一种轻量级的数据交换格式,采用一种“键:值”对的文本格式来存储和表示数据,在系统交换数据过程中常常被使用,是一种理想的数据交换语言。在使用Java做Web开发时,不可避免的会遇到Json的使用。
特点
- 在服务端和安卓客户端提供最佳性能
- 提供简单的 toJSONString() 和 parseObject() 方法来将 Java 对象转换为 JSON,反之亦然
- 允许预先存在的不可修改对象与 JSON 相互转换
- 对 Java 泛型的广泛支持
- 允许对象的自定义表示
- 支持任意复杂的对象(具有深层继承层次和泛型类型的广泛使用
Fastjson 源码地址:https://github.com/alibaba/fastjson
Fastjson 中文 Wiki:https://github.com/alibaba/fastjson/wiki/Quick-Start-CN
maven库地址:https://mvnrepository.com/artifact/com.alibaba/fastjson/2.0.14
优秀文章:
http://i.kimmking.cn/2017/06/06/json-best-practice/
https://www.runoob.com/json/json-tutorial.html
开发者:温绍锦
1、环境配置
你可以在 maven 中央仓库中直接下载:
https://repo1.maven.org/maven2/com/alibaba/fastjson/
或者配置 maven 依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>
其中 x.x.x 是版本号,根据需要使用特定版本,建议使用最新版本。
中央仓库:https://mvnrepository.com/artifact/com.alibaba/fastjson
2、应用
https://www.runoob.com/w3cnote/fastjson-intro.html
准备
package com.bean;
import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;
public class Person {
@JSONField(name = "AGE",serialize=false)//自定义转换 :nanm定义上的key serialize:指定字段不序列化 默认 fastjson序列化根据 fieldName 的字母序进行序列化
private int age;
//默认j变量名为json的key
private String fullName;
@JSONField(name="DATE OF BIRTH", format="dd/MM/yyyy", ordinal = 3) //自定义转换 format:用于格式化date属性 ordinal:指定字段的顺序 默认0
private Date dateOfBirth;
public Person(int age, String fullName, Date dateOfBirth) {
this.age = age;
this.fullName = fullName;
this.dateOfBirth = dateOfBirth;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
}
2.1 创建 JSON 对象
创建 JSON 对象非常简单,只需使用 JSONObject(fastJson提供的json对象) 和 JSONArray(fastJson提供json数组对象) 对象即可。
我们可以把JSONObject 当成一个 Map<String,Object> 来看,只是 JSONObject 提供了更为丰富便捷的方法,方便我们对于对象属性的操作。
public class JSONObject
extends JSON
implements Map<String, Object>, Cloneable, Serializable, InvocationHandler
JSONArray 当做一个 List,可以把 JSONArray 看成 JSONObject 对象的一个集合
public class JSONArray
extends JSON
implements List<Object>, Cloneable, RandomAccess, Serializable
由于 JSONObject 和 JSONArray 继承了 JSON,所以说也可以直接使用两者对 JSON 格式字符串与 JSON 对象及 javaBean 之间做转换,不过为了避免混淆我们还是使用 JSON。
// JSONObject和JSONArray转换
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < 2; i++) {
JSONObject jsonObject33 = new JSONObject();
jsonObject33.put("AGE", 10);
jsonObject33.put("FULL NAME", "Doe " + i);
jsonObject33.put("DATE OF BIRTH", "2016/12/12 12:12:12");
jsonArray.add(jsonObject33);
}
String jsonOutputz = jsonArray.toJSONString();
System.out.println("JSONObject和JSONArray转换:"+jsonOutputz);
/**
* [
* {
* "DATE OF BIRTH": "2016/12/12 12:12:12",
* "FULL NAME": "Doe 0",
* "AGE": 10
* },
* {
* "DATE OF BIRTH": "2016/12/12 12:12:12",
* "FULL NAME": "Doe 1",
* "AGE": 10
* }
* ]
*/
2.2 对象转换为 JSON
Java 对象转换换为 JSON 对象:JSON.toJSONString()
案例:
import com.alibaba.fastjson.JSON;
import com.bean.Person;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class testjson {
private List<Person> listOfPersons = new ArrayList<Person>();
//执行@test前执行
@Before
public void setUp() {
listOfPersons.add(new Person(15, "John Doe", new Date()));
listOfPersons.add(new Person(20, "Janette Doe", new Date()));
}
@Test
public void whenJavaList_thanConvertToJsonCorrect() {
//JSON.toJSONString() Java 对象转换换为 JSON 对象
String jsonString = JSON.toJSONString(new Person(15, "John Doe", new Date()));
System.out.println("Person类转jsonString:"+jsonString);
String jsonOutput= JSON.toJSONString(listOfPersons);
System.out.println("list<Person类>:"+jsonOutput);
//
}
}
运行结果如下:
Person类转jsonString:{"fullName":"John Doe","DATE OF BIRTH":"05/12/2021"}
list<Person类>:[{"fullName":"John Doe","DATE OF BIRTH":"05/12/2021"},{"fullName":"Janette Doe","DATE OF BIRTH":"05/12/2021"}]
2.3 JSON 转换为对象
JSON.parseObject
实例类
package com.bean;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Date;
public class Person {
@JSONField(name = "AGE")//自定义转换 :nanm定义上的key serialize=false serialize:指定字段不序列化 默认 fastjson序列化根据 fieldName 的字母序进行序列化
private int age;
//默认j变量名为json的key
private String fullName;
@JSONField(name="DATE OF BIRTH",ordinal = 3,format = "dd/MM/yyyy" ,deserialize=false) //自定义转换 format = "dd/MM/yyyy" format:用于格式化date属性 ordinal:指定字段的顺序 默认0
@JsonFormat(shape = JsonFormat.Shape.STRING,pattern="yyyy-MM-dd HH:mm:ss")
private Date dateOfBirth;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", fullName='" + fullName + '\'' +
", dateOfBirth=" + dateOfBirth +
'}';
}
public Person(int age, String fullName, Date dateOfBirth) {
this.age = age;
this.fullName = fullName;
this.dateOfBirth = dateOfBirth;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
}
转换
//JSON转换为Java,时间未格式化
String zhO = "{\"AGE\":20,\"fullName\":\"John\",\"DATE OF BIRTH\":1638681972980}";
Person newPerson = JSON.parseObject(zhO, Person.class);
System.out.println("转Person::"+newPerson);
//Json转java,格式化时间:yyyy-MM-dd HH:mm:ss
// 实例date类上 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern="yyyy-MM-dd HH:mm:ss")
String sss = "{\"AGE\":20,\"fullName\":\"John\",\"DATE OF BIRTH\":\"2020-01-08 22:41:54\"}";
System.out.println("转化数据:"+sss);
Person newPerson2 = JSON.parseObject(sss, Person.class);
System.out.println("转Person2::"+newPerson2);
2.4 json转换List集合
JSON.paresArray()
// 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);
2.5 json转换为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>>() {});
2.6 获取json某个值
JSONObject object1 = new JSONObject();
//1.在JSONObject对象中放入键值对
object1.put("name", "张三");
object1.put("name1", "张三1");
object1.put("name2", "张三2");
System.out.println(object1);
//2.根据key获取value
String name = (String) object1.get("name");
System.out.println(name)
3、固定枚举注解
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 |
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);
WriteNullNumberAsZero
// {"age":10,"id":0,"name":"张三"}
String s = JSON.toJSONString(stu);
// {"age":10,"id":0,"name":"张三"}
String s = JSON.toJSONString(stu, SerializerFeature.WriteNullNumberAsZero);
WriteNullStringAsEmpty
// {"age":10,"id":0,"name":"张三"}
String s = JSON.toJSONString(stu);
// {"address":"","age":10,"email":"","name":"张三"}
String s = JSON.toJSONString(stu, SerializerFeature.WriteNullStringAsEmpty);
WriteNullBooleanAsFalse
// {"age":10,"id":0,"name":"张三"}
String s = JSON.toJSONString(stu);
// {"age":10,"isBoolean":false,"name":"张三"}
String s = JSON.toJSONString(stu, SerializerFeature.WriteNullBooleanAsFalse);
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);
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;
}
4、使用 ContextValueFilter 配置 JSON 转换
在某些场景下,对Value做过滤,需要获得所属JavaBean的信息,包括类型、字段、方法等。在fastjson-1.2.9中,提供了ContextValueFilter,类似于之前版本提供的ValueFilter,只是多了BeanContext参数可用。
//过滤
String jsonOutputaf = JSON.toJSONString(listOfPersons);
System.out.println("jsonOutputaf>>>>>:"+jsonOutputaf);
ContextValueFilter valueFilter = new ContextValueFilter() {
@Override
public Object process(BeanContext beanContext, Object o, String name, Object value) {
if (name.equals("DATE OF BIRTH")) { //key =DATE OF BIRTH 改为NOT TO DISCLOSE
return "NOT TO DISCLOSE";
}
if (value.equals("John Doe")) { //值等于John Doe是大写JOHN DOE
System.out.println(((String) value).toUpperCase());
return ((String) value).toUpperCase();
}else {
return value;
}
}
};
String jsonOutputFilter = JSON.toJSONString(listOfPersons, valueFilter);
System.out.println("jsonOutputFilter::::"+jsonOutputFilter);