fastjson 是阿里巴巴的开源 JSON 解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。
功能完备: 支持泛型,支持流处理超大文本,支持枚举,支持序列化和反序列化扩展。
下载 jar 包 或者配置 maven 依赖:
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency>
|
创建 JSONObject 对象
创建 JSON 对象非常简单,只需使用 JSONObject(fastJson提供的json对象) 和 JSONArray(fastJson 提供 json 数组对象) 对象即可。
我们可以把 JSONObject 当成一个 Map<String,Object> 来看,只是 JSONObject 提供了更为丰富便捷的方法,方便我们对于对象属性的操作。
- 直接构造
1 2 3 4
| JSONObject json = new JSONObject(); json.put("aaa", "title"); json.put("bbb", 2); json.put("ccc", new Object());
|
2. 查看源码, 可知构造的时候传入 map
1 2 3 4 5 6 7
| public JSONObject(Map<String, Object> map){ if (map == null) { throw new IllegalArgumentException("map is null."); } this.map = map; }
|
3. JSON.parseObject 方法
1 2
| JSONObject obj = JSON.parseObject("{\"title\":\"fastJson真的很快\"}");
|
创建 JSONArray 对象
同样我们可以把 JSONArray 当做一个 List<Object>
,可以把 JSONArray 看成 JSONObject 对象的一个集合。
- 直接构造
1 2
| JSONArray json = new JSONArray(); json.add("xxx");
|
2. 查看源码, 可知构造的时候传入 list
1 2 3 4
| public JSONArray(List<Object> list){ this.list = list; }
|
3. JSON.parseArray 一系列方法
1 2
| JSONArray arr = JSON.parseArray("[\"一只兔子\",\"两只绵羊\"]\n");
|
对象转 json 串 (序列化)
com.alibaba.fastjson.JSON 的静态方法, 适用于任意 Object 对象.包括 JSONObject, JSONArray
1 2
| String text = JSON.toJSONString(obj);
|
由于 JSONObject 和 JSONArray 都继承与JSON抽象类, 如果直接得到了 JSON 抽象类的子类, 可以直接 toString 或者 toJSONString 方法(两者等价)进行序列化.
序列化默认情况严格依赖 bean 的 Getter 方法,所以一定要规范 Getter 方法的写法。
标准写法
1 2 3
| public String getFullName() { return this.fullName; }
|
错误写法
1 2 3
| protected String getFullName() { return this.fullName; }
|
错误写法
1 2 3
| String getFullName() { return this.fullName; }
|
错误写法
1 2 3
| public String getFullName(String fullName) { return this.fullName; }
|
错误写法
1 2 3
| private String getFullName(int a, int b) { return this.fullName; }
|
1 2
| String jsonString = jsonObject.toString()); String jsonArrayString = jsonArray.toJSONString();
|
序列化定制之 SerializerFeature
JSON.toJSONString 的一些定制功能
1 2 3
| System.out.println(JSON.toJSONString(person, SerializerFeature.BeanToArray)); System.out.println(JSON.toJSONString(new Person(18, null, null), SerializerFeature.WriteNullStringAsEmpty));
|
序列化定制之 SerializeFilter
NameFilter 对序列化后的参数名进行拦截处理。
1
| Object process(Object object, String name, Object value);
|
这个过滤器是用来拦截最后确定序列化的参数名时被调用的。返回值为最终确认的参数名,如果不做处理,那就直接返回name。
ValueFilter 对序列化后的value进行拦截处理。
1
| String process(Object object, String name, Object value);
|
对于参数列表中三个参数都很好理解,第一个Object为现在被拦截参数的拥有者,第二个参数为其参数名,第三个参数为其参数值,其实这个地方重点要理解的是返回值。ValueFilter的作用是在序列化之前对一些参数值做一些后置处理。例如参数值的类型为 Double,但是你想做精度控制并且返回值为 String,这个 ValueFilter 就有很大作用了,如果不做处理,直接返回 value 就可以了。
ContextValueFilter extends SerializeFilter
在某些场景下,对Value做过滤,需要获得所属 JavaBean 的信息,包括类型、字段、方法等。在 fastjson-1.2.9 中,提供了 ContextValueFilter,类似于之前版本提供的ValueFilter,只是多了BeanContext参数可用。
1 2 3 4 5 6 7 8
| package com.alibaba.fastjson.serializer;
public interface ContextValueFilter extends SerializeFilter { Object process(BeanContext context, Object object, String name, Object value); }
|
json 串转对象 (反序列化)
1
| VO vo = JSON.parseObject("{...}", VO.class);
|
注意反序列化时为对象时,必须要有默认无参的构造函数,否则会报异常. 新版本不会报异常, 但是最好加上. 记住任何时候加空构造都是个好习惯.
配置类
JSONField 类的说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.alibaba.fastjson.annotation;
public @interface JSONField {
int ordinal() default 0;
String name() default "";
String format() default "";
boolean serialize() default true;
boolean deserialize() default true; }
|
- JSONField 注解可作用与 Field 或者 方法上, 也可以是 Setter (用于反序列化)和 Getter(序列化) 方法.
- 一个简单的使用就是
@JSONField(name = "abc")
, 序列化和反序列话讲使用abc这个字段, 否则会使用成员变量的名字
举例JSONField(name = "DATE OF BIRTH", format="yyyy-MM-dd HH:mm:ss", serialize = true, ordinal = 1)
- JSONField 的 format 参数用于格式化 Date 类型。
1 2 3
| @JSONField(format="yyyy-MM-dd") public Date date;
|
- 默认情况下, FastJson 库可以序列化 Java bean 实体, 但我们可以使用 JSONField 的 serialize 指定字段不序列化。
- 使用 JSONField 的 ordinal 参数指定字段的顺序. ordinal = 1表示排在第一列.
注意:FastJson 在进行操作时,是根据 getter 和 setter 的方法进行的,并不是依据 Field 进行。建议正常情况下选取注解field上即可. 不要两种都选取.
若属性是私有的,必须有 set 方法且set方法要书写正确。否则不会按照预期反序列化。得不到该值, 该值会为 null.
get 用于序列化成字符串. 若属性是私有的, 必须有 set 方法且get方法要书写正确. 否则该字段会被忽略掉!!!
SerializeConfig
SerializeConfig:内部是个map容器主要功能是配置并记录每种Java类型对应的序列化类。
举例使用:
1
| SerializeConfig.getGlobalInstance().addFilter(A.class, upcaseNameFilter);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class ClassNameFilterTest extends TestCase { public void test_filter() throws Exception { NameFilter upcaseNameFilter = new NameFilter() { public String process(Object object, String name, Object value) { return name.toUpperCase(); } }; SerializeConfig.getGlobalInstance() .addFilter(A.class, upcaseNameFilter); Assert.assertEquals("{\"ID\":0}", JSON.toJSONString(new A())); Assert.assertEquals("{\"id\":0}", JSON.toJSONString(new B())); } public static class A { public int id; } public static class B { public int id; } }
|
fastjson 的一些用法
用 fastjson 实现克隆
1
| JSON.parseObject(JSON.toJSONString(this), this.getClass());
|
将对象中的null赋值为""
1 2
| Object object = xxx; JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteNullStringAsEmpty), object.getClass());
|
Fastjson 的 SerializerFeature 序列化属性
QuoteFieldNames———-输出key时是否使用双引号,默认为true
WriteMapNullValue——–是否输出值为null的字段,默认为false
WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
示例
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| package qy.likai.demo;
import java.util.Collections; import java.util.Date; import java.util.List;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.serializer.SerializerFeature;
public class App {
public static void main( String[] args) { JSONObject json = new JSONObject(); json.put("aaa", "title"); json.put("bbb", 2); json.put("ccc", 3.1415); json.put("ddd", true); json.put("eee", null); json.put("fff", new Object()); json.put("ggg", new int[]{1, 2, 3}); json.put("hhh", Collections.singletonList(666)); json.put("iii", Collections.singletonMap("my", "myvalue")); json.put("jjj", Boolean.TRUE); json.put("kkk", 'k');
System.out.println(json.toString()); System.out.println(json.toJSONString()); System.out.println(JSON.toJSONString(json));
final Person person = new Person(18, "zhangsan", new Date()); System.out.println(JSON.toJSONString(person)); System.out.println(JSON.toJSONString(person, SerializerFeature.BeanToArray)); System.out.println(JSON.toJSONString(new Person(18, null, null))); System.out.println(JSON.toJSONString(new Person(18, null, null), SerializerFeature.WriteNullStringAsEmpty));
Object object = JSON.parse("{\"runoob\":\"菜鸟教程\"}"); JSONObject jsonObject = (JSONObject)object; jsonObject = JSON.parseObject(json.toJSONString());
object = JSON.parse("[\"菜鸟教程\",\"RUNOOB\"]\n"); JSONArray arr = (JSONArray)object; System.out.println("arr1: " + arr); arr = JSON.parseArray("[\"菜鸟教程\",\"RUNOOB\"]\n"); System.out.println("arr2: " + arr);
Person p = JSON.parseObject("{\"AGE\":18,\"FULL NAME\":\"zhangsan\",\"birthday\":\"2020-04-25 12:12:53\"}", Person.class);
List<Integer> list = JSON.parseArray("[1,2,3]", Integer.class); System.out.println(list); }
public static class Person {
@JSONField(name = "AGE") private int age;
@JSONField(name = "FULL NAME") private String fullName;
@JSONField(name = "DATE OF BIRTH") private Date dateOfBirth;
public Person(int age, String fullName, Date dateOfBirth) { super(); 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 this.fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public Date getDateOfBirth() { return dateOfBirth; }
public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
} }
|
fastjson 处理枚举
很多人也喜欢给枚举定义一个私有的属性,序列化为 JSON 时,希望以这个属性值作为 value,这个时候就需要自己定 JSON 的序列化和反序列化实现了。Fastjson 提供了 2 个接口。用户控制序列化和反序列化行为,这个实在是太简单,这里不多说。看代码
- ObjectSerializer
- ObjectDeserializer
自定义 ObjectSerializer / ObjectDeserializer 的方式最为灵活,可以考虑抽象一个接口出来,让所有的枚举都实现接口。这样针对接口编写 ObjectSerializer /ObjectDeserializer 实现,就可以很好的复用了。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| public static class GenderEnumSerializer implements ObjectSerializer {
@Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { Gender gender = (Gender) object; serializer.out.writeString(gender.getName()); } }
public static class GenderEnumDeserializer implements ObjectDeserializer {
@SuppressWarnings("unchecked") @Override public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { String value = parser.parseObject(String.class); for (Gender gender : Gender.values()) { if (gender.getName().equals(value)) { return (T) gender; } } return null; }
@Override public int getFastMatchToken() { return JSONToken.LITERAL_STRING; } }
enum Gender {
BOY("男"), GIRL("女");
public final String name;
Gender(String name) { this.name = name; } public String getName() { return name; } }
static class User {
private Integer id;
@JSONField(serializeUsing = GenderEnumSerializer.class, deserializeUsing = GenderEnumDeserializer.class) private Gender gender;
public User() { }
public User(Integer id, Gender gender) { super(); this.id = id; this.gender = gender; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Gender getGender() { return gender; }
public void setGender(Gender gender) { this.gender = gender; } }
|
调用代码
1 2 3 4 5 6 7 8
| User user = new User(10002, Gender.BOY); String jsonString = JSON.toJSONString(user); System.out.println(jsonString);
user = JSON.parseObject(jsonString, User.class); System.out.println(user.getGender());
|
fastjson 处理布尔值
建议 POJO 中布尔值一律定义为 Boolean 类型,且都不要加 is 前缀,防止一些框架解析引起的序列化错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static class Person {
private Boolean male;
public Boolean getMale() { return male; }
public void setMale(Boolean male) { this.male = male; }
@Override public String toString() { return "Person [male=" + male + "]"; } }
|
参考
Fastjson 项目地址
https://github.com/alibaba/fastjson
Fastjson 简明教程
https://www.runoob.com/w3cnote/fastjson-intro.html
Fastjson处理枚举 - 技术交流 - SpringBoot中文社区
https://springboot.io/t/topic/3648