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是用于序列化后返回给前端的字段名称
有几点需要注意:

  1. 如果不写 @JSONField注解,则默认使用Java类的字段名称来匹配JSON文件的字段名称,如apiId字段必须匹配的是json文件的apiId,但是这里提供的是api_id,所以必须加上反序列化属性alternateNames = "api_id"
  2. 如果不写 @JSONField注解,默认返回给前端的JSON,即序列化之后的JSON,字段名也是Java类的属性名,即apiId,要想迎合甲方或者客户,就必须使用 name属性;
  3. 如果只有属性name, 而没有属性alternateNames,则正反序列化都是使用name的值,即匹配JSON文件的字段和返回给前端的字段,都是api_id

(3)不需要解析和返回的字段
有时候为了提高处理速度,或者不希望某些字段值对外公开,则需要进行过滤,读取JSON文件时(反序列化),如果不希望读取某个字段,则可以在该字段的@JSONField注解上加上属性deserialize = false,这样Java对象中该属性的值就是null,而如果不希望对外暴露某个字段的值,则添加属性serialize = false,这样前端人员或者Spider就不会拿到这些属性的值了。
PS:其实关于隐藏字段的方式Springboot默认的JSON处理器jackson中是有对应的注解和属性的,即@JsonIgnore,但是转换器HttpMessageConverter只能选择一个,这里我们选择的是FastJsonHttpMessageConverter,所以jackson的使用方式不在讨论之列。

posted on 2022-03-10 21:05  高冷的恒哥  阅读(502)  评论(0编辑  收藏  举报