fastjson的字段
fastjson是Alibaba开源的一套JSON处理工具类,用起来很方便,在项目中经常会使用到,强大的注解可以将JSON和Java对象玩出花来,在使用fastjson之前,需要先搞清楚几个概念:
(1)序列化与反序列化
往简单了说,序列化指的就是将Java对象转化为字符串/文件/流,总之就是将Java对象设置为可以看到的形式,而反序列化,就是将文件/流/字符串解析为Java对象。工作中比较常用的序列化和反序列化包括如下几个场景:
- 第三方丢给你一个*.json文件,然后要保存到数据库中
- 从数据库读取大量数据,传输给前端或者第三方人员
- 读取Excel文件,然后处理Excel(修改值/保存到数据库)
以上这些场景,先不管数据量多少的问题,单单是这么多单位的命名风格就各地迥异,而且可能是其他语言如Python、C++生成的,没有一个统一的规范,python是下划线分隔,Excel是中文,这还仅仅是读取。对于发送给第三方使用,对方也会要求使用固定的字段名称,我们能决定的,只是Java实体类的字段名(驼峰命名法),但是这又跟数据的处理没有关系。
(2)fastjson的注解——@JsonField
注解@JsonField
可以做到读取json文件的是否适配字段A_b(反序列化),提供json的是否给字段aB(序列化),这一切都是靠的注解@JsonField
,该注解具有如下属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface JSONField {
int ordinal() default 0; //是根据fieldName的字母序进行序列的,你可以通过ordinal指定字段的顺序
String name() default ""; //序列化和反序列化时候的别名
String format() default ""; //用于字符串格式的日期转换
boolean serialize() default true; // 是否参与序列化
boolean deserialize() default true; //是否参与反序列化
SerializerFeature[] serialzeFeatures() default {}; //序列化选项 SerializerFeature.WriteNullNumberAsZero 如空Number填充0
Feature[] parseFeatures() default {}; //反序列化选项
String label() default ""; //标签,
boolean jsonDirect() default false; //当你有⼀个字段是json字符串的数据,你希望直接输出,⽽不是经过转义之后再输出。
Class<?> serializeUsing() default Void.class; // 属性的序列化类,可定制。可有现存的,比如本来是Long,序列化的时候转为String:serializeUsing= ToStringSerializer.class
Class<?> deserializeUsing() default Void.class; // 属性的反序列化类,可定制。
String[] alternateNames() default {}; //参与反序列化时候的别名
boolean unwrapped() default false; // 对象映射到父对象上。不进行子对象映射。简单而言,就是属性为对象的时候,属性对象里面的属性直接输出当做父对象的属性输出
String defaultValue() default ""; //设置默认值
}
很明显,从注释中可以看出,@JsonField
的属性也是分为序列化和反序列化的,面对不同的场景,要选用不同的属性,下面结合具体的实例来讲解fastjson的使用
- 项目类型:Springboot,且默认的JSON转换器设置为
fastjson
- 设置默认的JSON Convertor
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setCharset(Charset.forName("UTF-8"));
config.setDateFormat("yyyyMMdd HH:mm:ssS");
//设置允许返回为null的属性
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
fastJsonConverter.setFastJsonConfig(config);
List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON_UTF8);
fastJsonConverter.setSupportedMediaTypes(list);
converters.add(fastJsonConverter);
}
}
- 实体类
@NoArgsConstructor
@Data
public class ProfileRecord {
@JSONField(name = "api_id", alternateNames = "api_id")
private String apiId;
@JSONField(name = "code", deserialize = false)
private Integer code;
@JSONField(name = "data", serialize = false, deserialize = false)
private DataDTO data;
@JSONField(name = "msg")
private String msg;
@JSONField(name = "requestId")
private String requestId;
@JSONField(name = "version")
private String version;
}
}
- json文件
{
"api_idd": "10086",
"code": 0,
"msg": "成功",
"requestId": "a8fa3ebb959793ed",
"version": "2022-03-10"
}
为了方便,直接在Controller层中读取json文件
@RestController
@RequestMapping("/profile")
public class ProfileRecordController {
@SneakyThrows
@GetMapping("/list")
public ProfileRecord get() {
InputStream inputStream = FileReadUtil.getInstance()
.getInputStream("static/profileRecord.json");
String string = FileReadUtil.getInstance().getString(inputStream);
ProfileRecord profileRecord = JSONObject.parseObject(string, ProfileRecord.class);
inputStream.close();
return profileRecord;
}
}
关于上面的FileReadUtil
工具类,是一个专门读取文件的类,可以参考本人的另一篇文章,
(如果读者有更好的读取JSON文件的方式,可以在评论区交流)
然后返回字符串,这里读取的是json文件,所以返回的字符串也就是json字符串,这种方式称之为反序列化,所以在java类中需要在字段上配置alternateNames
属性
@JSONField(name = "api_id", alternateNames = "api_id")
private String apiId;
那为什么这里有name
属性呢,是因为name
是用于序列化后返回给前端的字段名称
有几点需要注意:
- 如果不写
@JSONField
注解,则默认使用Java类的字段名称来匹配JSON文件的字段名称,如apiId
字段必须匹配的是json文件的apiId
,但是这里提供的是api_id
,所以必须加上反序列化属性alternateNames = "api_id"
- 如果不写
@JSONField
注解,默认返回给前端的JSON,即序列化之后的JSON,字段名也是Java类的属性名,即apiId
,要想迎合甲方或者客户,就必须使用name
属性; - 如果只有属性
name
, 而没有属性alternateNames
,则正反序列化都是使用name
的值,即匹配JSON文件的字段和返回给前端的字段,都是api_id
(3)不需要解析和返回的字段
有时候为了提高处理速度,或者不希望某些字段值对外公开,则需要进行过滤,读取JSON文件时(反序列化),如果不希望读取某个字段,则可以在该字段的@JSONField
注解上加上属性deserialize = false
,这样Java对象中该属性的值就是null
,而如果不希望对外暴露某个字段的值,则添加属性serialize = false
,这样前端人员或者Spider就不会拿到这些属性的值了。
PS:其实关于隐藏字段的方式Springboot默认的JSON处理器jackson
中是有对应的注解和属性的,即@JsonIgnore
,但是转换器HttpMessageConverter
只能选择一个,这里我们选择的是FastJsonHttpMessageConverter
,所以jackson
的使用方式不在讨论之列。