JSON 学习(FastJson和Jackson)

JSON 学习

文章目录

1. Json数据格式

JSON官网:https://www.json.org/json-en.html

  JSON(JavaScript Object Notation)是一种轻量级数据交换格式。轻量级指的是跟xml作比较,数据交换指的是客户端和服务器之间业务数据的传输格式。
  JSON 是一种完全独立于语言的文本格式,使用 C 系列语言(包括 C)所熟悉的约定,使之让开发者很容易阅读和编写,也让机器很容易解析和生成,并有效地提升网络传输效率。这些优点也使 JSON 成为一种理想的独立语言文本格式。很多语言都提供了对json的支持(包括C,C++,C#,Java,JavaScript、Perl、Python等)。
json

1.1 Json 数据格式

  Json的数据本身是数组,中括号包裹,数组的元素之间逗号分开,数组元素的数据类型没有限制。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JsonArray_Json数组格式</title>
    <script type="text/javascript">
        /**
         * 定义json数组格式
         * 中括号包裹,数组的元素的数据类型没用限制
         * 元素之间,逗号分开
         */
        var jsonArray = ["k1","中文",100,9.9,true];

        // 访问数组元素,通过索引访问
        console.log("索引访问:");
        console.log(jsonArray[1]);
        
        //遍历数组,取出数组中的元素
        console.log("遍历数组:");
        for (var i = 0; i < jsonArray.length; i++) {
            console.log(jsonArray[i]);
        }
    </script>
</head>
<body>

</body>
</html>

输出:鼠标放到页面上 右键 -> 选择检查> 或 直接按 F12>

json

1.2 Json 对象格式

  json的数据本身是对象,大括号包裹,对象采用键值对形式存储,键固定为字符串类型,值是任意类型的数据,键和值使用冒号分开。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JsonObject_Json对象格式</title>
    <script type="text/javascript">
        /**
         * 定义json的对象格式
         * 大括号包裹,定义键值对,建必须是字符串类型,值的数据类型不限
         * 键值对之间,是冒号分开
         * 每个键值对之间,逗号分开
         */
        var jsonObject = {"k1":"v1","k2":"中国","k3":100,"k4":9.9,"k5":true};
        //取出键值对,键找值的方式
        console.log("取出键值对:");
        console.log(jsonObject.k1);
        console.log(jsonObject.k2);
        console.log(jsonObject.k3);
        console.log(jsonObject.k4);
        console.log(jsonObject.k5);
    </script>
</head>
<body>

</body>
</html>

json

1.3 数组对象相互嵌套格式

1.3.1 数组中的元素是对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JsonArrayObject_Json数据的嵌套</title>
</head>
<body>
<script type="text/javascript">
    /**
     * json数组的元素是对象
     * 数组定义了2个元素,每个元素是对象
     * 对象是键值对形式
     */
    var jsonArray =[
        {"name":"张三","age":20},
        {"name":"李四","age":22}
    ];
    //取出需要的数据,李四 22
    console.log("取出需要的数据:");
    console.log(jsonArray[1].name + "=="+jsonArray[1].age);
    //遍历数组,取出数组中的元素
    console.log("遍历数组,取出需要的数据:");
    for (var i = 0; i < jsonArray.length; i++) {
        console.log(jsonArray[i].name + "===" + jsonArray[i].age);
    }
</script>
</body>
</html>

json

1.3.2 对象中的值是数组
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JsonObjectArray</title>
</head>
<body>
<script type="text/javascript">
    /**
     * json数组是对象,对象值是数组
     */
    var jsonObject = {
      "k1":["北京","天津","上海"],
      "k2":["中国","美国","英国"]
    };
    //取出上海
    console.log("取出“上海“值:");
    console.log(jsonObject.k1[2])
    //分别取出k1和k2键的数组,遍历
    console.log("分别取出k1和k2键的数组,遍历:");
    for (var i = 0; i < jsonObject.k1.length; i++) {
        console.log(jsonObject.k1[i]);
    }
    console.log("========");

    for (var i = 0; i < jsonObject.k2.length; i++) {
        console.log(jsonObject.k2[i]);
    }
</script>
</body>
</html>

json

1.3.3 你中有我,我中有你
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Json数据的嵌套</title>
</head>
<body>
<script type="text/javascript">
    /**
     * json的数据嵌套,泥中有我,我中有你
     * json的数据本质上是对象 {}  {"属性":"属性值,"属性2”:“属性2”}
     * 集合:【】
     * 对象的键是字符串,对象的值是数组、数组的元素是对象
     */

    var json = {
      //键是k1,值是数组,数组元素是对象
        "k1":[
            //数组的元素是2个对象
            {"name":"张三","age":20},
            {"name":"李四","age":22}
        ],
        "k2":[
            {"name":"王五","age":24},
            {"name":"赵六","age":26}
        ]
    };

    //取出数据 李四 22
    console.log("取出数据————李四 22")
    console.log(json.k1[1].name + "==" + json.k1[1].age);

    //遍历k2键对应的数组
    console.log("遍历k2键对应的数组")
    for (var i = 0; i < json.k2.length; i++) {
        console.log(json.k2[i].name + "==" +  json.k2[i].age)
    }

</script>
</body>
</html>

json

2. 使用场景

  • 网络传输
  • 序列化存储

描述同样的消息,json相比xml占用更少的空间,如:

xml格式

<?xml version="1.0" encoding="UTF-8" ?>
<user>
    <id>1</id>
    <name>张三</name>
    <age>30</age>
</user>

json表示

{
  "id": 1,
  "name": "张三",
  "age": 30
}

3. java里面操作json有哪些技术

  所有的操作把java里面的bean、map、collection等转化为json字符串(序列化),或反向操作(反序列化)占用更少的空间,如:java里面作json的技术一览
json官网:https://www.json.org/json-en.html

json

4. Fastjson

  FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。FastJson会出现漏洞和bug,注意一下!

json

4.1 FastJson的优点

  • 速度快
    Fastjson相对其他JSON库特点是快,从2011年fastjson发布1.1.版本之后,其性能从未被其他Java实现的JSON超越
  • 使用广泛
    Fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受,在2012年被开源中国评选为最受欢迎的国产开源软件之一。
  • 测试完备
    Fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个,每次发布都会进行回归测试,保证质量稳定。
  • 使用简单
    Fastjson的API十分简洁
  • 功能完备
    支持泛型,支持流处理超大文本、支持枚举、支持序列化和反序列化扩展。

4.2 FastJson的使用

4.2.1 FastJson引入依赖
<!--https://mvnrepository.com/artifact/com.alibaba/fastjson-->
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.68</version>
           <scope>compile</scope>
       </dependency>
4.2.2 创建实体类
package com.test.json.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class Student {

    private Integer id;

    private String name;

    private Integer age;

    private String email;

    private Date birthday;
    
    private Boolean flag;
}
4.2.3 序列化

序列化:是指将java对象转成json格式字符串的过程,JavaBean对象,List集合对象,Map集合,为应用最广泛的。

  • JSON.toJSONString
    • 序列化java对象
4.2.3.1 创建测试类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.test.json.pojo.Student;
import org.junit.Test;

import java.util.*;

public class TestFastJson {

    //用于获取日期
     public Date getDate(){
        Date date = new Date();
        return date;
    }
}
4.2.3.2 Java中的对象(Student对象),序列化为Json格式字符串
    @Test
    //Java中的对象,Student对象,序列化为Json格式字符串
    public void testObjectToJson(){
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setEmail("zs@sina.com");
        student.setBirthday(getDate()); //转化毫秒值
        //student 对象,转到Json格式字符串
        //调用静态方法,传递要转换的对象
        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);
        //student对象,转到Json格式字符串
        //{"age":20,"birthday":1662539443005,"email":"zs@sina.com","id":1,"name":"张三"}
    }
4.2.3.3 Java中的集合List,序列化为Json格式字符串
    @Test
    //Java中的集合List,序列化为Json格式字符串
    public void testListToJson(){
        //集合List,存储Student对象
        List<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("张三");
        student1.setAge(20);
        student1.setEmail("zs@sina.com");
        student1.setBirthday(getDate()); //转化毫秒值

        Student student2 = new Student();
        student2.setId(2);
        student2.setName("李四");
        student2.setAge(22);
        student2.setEmail("ls@126.com");
        student2.setBirthday(getDate()); //转化毫秒值
        //Student对象存储到List集合中
        list.add(student1);
        list.add(student2);
        //List集合,序列化为Json格式字符串
        String jsonString = JSON.toJSONString(list);
        System.out.println(jsonString);
        // 转后的结果是数组,数组的元素是对象
        // [{"age":20,"birthday":1662539828574,"email":"zs@sina.com","id":1,"name":"张三"}, //数组是1对象:张三 2对象:李四
        // {"age":22,"birthday":1662539828574,"email":"ls@126.com","id":2,"name":"李四"}]
    }
4.2.3.4 Java中的集合Maps,序列化为Json格式字符串
    @Test
    //Java中的集合Maps,序列化为Json格式字符串
    public void testMapToJson(){
        //创建Map集合,键为字符串类型,值是Student对象
        Map<String,Student> map = new HashMap<String,Student>();
        //集合List,存储Student对象
        List<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("张三");
        student1.setAge(20);
        student1.setEmail("zs@sina.com");
        student1.setBirthday(getDate()); //转化毫秒值

        Student student2 = new Student();
        student2.setId(2);
        student2.setName("李四");
        student2.setAge(22);
        student2.setEmail("ls@126.com");
        student2.setBirthday(getDate()); //转化毫秒值
        map.put("Student1",student1);
        map.put("Student2",student2);

        String jsonString = JSON.toJSONString(map);
        System.out.println(jsonString);
        // json格式字符串是对象,对象中有两个键 student1 student2 每个键对应的值是Student对象 List转换是数组 map转换是对象
        // {"Student1":{"age":20,"birthday":1662540245268,"email":"zs@sina.com","id":1,"name":"张三"},
        // "Student2":{"age":22,"birthday":1662540245268,"email":"ls@126.com","id":2,"name":"李四"}}
    }
4.2.4 反序列化
4.2.4.1 Json格式字符串,反序列化回到Java对象
    @Test
    //Json格式字符串,反序列化回到Java对象
    public void testJsonToObject(){
        String jsonString = "{\"age\":20,\"birthday\":1662539443005,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"}";
        //JSON类的静态方法
        //传递要反序列化的Json字符串,传递Java对象的class对象
        Student student = JSON.parseObject(jsonString,Student.class);
        System.out.println(student);
        //Student(id=1, name=张三, age=20, email=zs@sina.com, birthday=Wed Sep 07 16:30:43 CST 2022)
    }
4.2.4.2 Json格式字符串,反序列化回到List集合
    @Test
    //Json格式字符串,反序列化回到List集合
    public void testJsonToList(){

        String jsonString = "[{\"age\":20,\"birthday\":1662539828574,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"},{\"age\":22,\"birthday\":1662539828574,\"email\":\"ls@126.com\",\"id\":2,\"name\":\"李四\"}]";

        //JSON类的静态方法,parseArray
        //传递json格式字符串,传递转换后的集合的泛型的class对象
        List<Student> list = JSON.parseArray(jsonString,Student.class);
        for (Student student:list){
            System.out.println(student);
        }
        // Student(id=1, name=张三, age=20, email=zs@sina.com, birthday=Wed Sep 07 16:37:08 CST 2022)
        // Student(id=2, name=李四, age=22, email=ls@126.com, birthday=Wed Sep 07 16:37:08 CST 2022)
    }
4.2.4.3 Json格式字符串,反字符串回到Map集合
    @Test
    //Json格式字符串,反字符串回到Map集合
    public void testJsonToMap(){
        String jsonString = " {\"Student1\":{\"age\":20,\"birthday\":1662540245268,\"email\":\"zs@sina.com\",\"id\":1,\"name\":\"张三\"},\"Student2\":{\"age\":22,\"birthday\":1662540245268,\"email\":\"ls@126.com\",\"id\":2,\"name\":\"李四\"}}";

        //JSON类的静态方法 .parseObject()
        //直接进行反序列化,Map集合没有泛型的,泛型没有是不安全的集合
        //转换后的集合,必须有泛型
        //调用parseObject,传递参数,类型引用 typeReferences类的泛型中,传递转后的map集合 protected 授权限保护的匿名方法 需要匿名内部类 是子类的匿名对象
        Map<String,Student> map = JSON.parseObject(jsonString,new TypeReference<Map<String,Student>>(){});

        for (String key: map.keySet()) {
            System.out.println(key + "::" + map.get(key));
        }
        // Student1::Student(id=1, name=张三, age=20, email=zs@sina.com, birthday=Wed Sep 07 16:44:05 CST 2022)
        //Student2::Student(id=2, name=李四, age=22, email=ls@126.com, birthday=Wed Sep 07 16:44:05 CST 2022)
    }
4.2.5 SerializerFeature枚举

创建测试类:

import java.util.Date;

/**
 * SerializerFeature 枚举:进行序列化时,可以自己定义特殊需求
 * JSON静态方法 tJSONString()
 * 方法的参数:第一个是要序列化的对象
 * 方法的参数:第二个参数SerializerFeature枚举类型的可变参数
 * SerializerFeature枚举常量,做序列化的个性需求
 */
public class TestFastJson2 {

    public Date getDate(){
        Date date = new Date();
        return date;
    }
}

SerializerFeature枚举常量,该枚举类型支持序列化的一些特性数据类型定义:

public static String toJSONString(Object object, SerializerFeature... features) {
    return toJSONString(object, DEFAULT_GENERATE_FEATURE, features);
}
1. 枚举常量 WriteMapNullValue ,序列化null值的字段
    @Test
    //WriteMapNullValue 枚举中的常量,序列化null值的字段
    public void testWriteMapNullValue(){
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        //不赋值
        //student.setEmail("zs@sina.com");
        String jsonString1 = JSON.toJSONString(student);
        System.out.println(jsonString1);
        // {"age":20,"birthday":1662542783030,"id":1,"name":"张三"}

        //方法参数上,添加枚举类型
        String jsonString2 = JSON.toJSONString(student,SerializerFeature.WriteMapNullValue);
        System.out.println(jsonString2);
        // {"age":20,"birthday":1662542783030,"email":null,"id":1,"name":"张三"}
    }
2. 枚举常量 WriteNullStringAsEmpty字段为null,序列化为""
    @Test
    //WriteNullAsEmpty 枚举的常量,序列化为null的字段,值序列化为“”
    public void testWriteStringAsEmpty(){
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        //不赋值
        //student.setEmail("zs@sina.com");

        //方法的参数上,添加枚举类型
        String jsonString = JSON.toJSONString(student,SerializerFeature.WriteNullStringAsEmpty);
        System.out.println(jsonString);
        // {"age":20,"birthday":1662543749112,"email":"","id":1,"name":"张三"}
    }
3. 枚举常量 WriteNullNumberAsZero 字段为null序列化为0
    @Test
    //WriteNullNumberAsZero 枚举的常量 序列化字段值为null 序列化为0
    public void testWriteNullAsZero() {
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        //不赋值
        // student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        //不赋值
        //student.setEmail("zs@sina.com");

        //方法的参数上,添加枚举类型
        String jsonString = JSON.toJSONString(student,SerializerFeature.WriteNullNumberAsZero);
        System.out.println(jsonString);
        // {"age":0,"birthday":1662544065292,"id":1,"name":"张三"}
    }
4. 枚举常量 WriteNullBooleanAsFalse 字段值为null 输出为false
    @Test
    //WriteNullBooleanAsFalse 枚举的常量 序列化布尔值为null 序列华为false
    public void testWriteNullBooleanAsFalse() {
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        //不赋值
        //student.setEmail("zs@sina.com");

        //不赋值
        //student.setFlag(true);

        String jsonString1 = JSON.toJSONString(student);
        System.out.println(jsonString1);
        // {"age":20,"birthday":1662544479657,"id":1,"name":"张三"}

        //方法的参数上,添加枚举类型
        String jsonString2 = JSON.toJSONString(student,SerializerFeature.WriteNullBooleanAsFalse);
        System.out.println(jsonString2);
        // {"age":20,"birthday":1662544479657,"flag":false,"id":1,"name":"张三"}
    }
5. 枚举常量 WriteDateUseDateFormat ,日期格式化;枚举常量 PrettyFormat,格式化输出
   @Test
    // WriteDateUseDateFormat 枚举的常量,序列化 日期格式化
    // PrettyFormat 枚举常量,序列化,格式化输出
    public void testWriteDateUseDate_FormatPrettyFormat(){
        Student student = new Student();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        //不赋值
        //student.setEmail("zs@sina.com");

        //不赋值
        //student.setFlag(true);

        String jsonString1 = JSON.toJSONString(student);
        System.out.println(jsonString1);
        //{"age":20,"birthday":1662544849121,"id":1,"name":"张三"}

        //方法的参数上,添加枚举类型
        String jsonString2 = JSON.toJSONString(student,SerializerFeature.WriteDateUseDateFormat);
        System.out.println(jsonString2);
        //{"age":20,"birthday":"2022-09-07 18:00:49","id":1,"name":"张三"}

        //方法的参数上,添加枚举类型 可以加俩
        String jsonString3 = JSON.toJSONString(student,SerializerFeature.WriteDateUseDateFormat,SerializerFeature.PrettyFormat);
        System.out.println(jsonString3);
        /**
         * {
         * 	"age":20,
         * 	"birthday":"2022-09-07 18:00:49",
         * 	"id":1,
         * 	"name":"张三"
         * }
         */
    }
4.2.6 @JSONField注解
1. JSONField 源码

@JSONField注解,该注解作用于方法上字段上和参数上可在序列化和反序列化时进行特性功能定制,注解可以放在方法上、成员变量上、方法的参数上

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.alibaba.fastjson.annotation;

import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface JSONField {
    int ordinal() default 0;

    String name() default "";

    String format() default "";

    boolean serialize() default true;

    boolean deserialize() default true;

    SerializerFeature[] serialzeFeatures() default {};

    Feature[] parseFeatures() default {};

    String label() default "";

    boolean jsonDirect() default false;

    Class<?> serializeUsing() default Void.class;

    Class<?> deserializeUsing() default Void.class;

    String[] alternateNames() default {};

    boolean unwrapped() default false;

    String defaultValue() default "";
}
2. 创建实体类:
package com.test.json.pojo;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.util.Date;

@Data
public class Student_JsonField {

//@JSONField 注解属性: name 指定序列化后的名字
//@JSONField 注解属性:ordinal 序列化后的顺序,属性值越小,顺序靠前   
//@JSONField 注解属性:format 序列化后的格式
//@JSONField 注解属性:serialize 是否序列化该字段
//@JSONField 注解属性:deserialize 是否反序列化该字段
//@JSONField 注解属性:serialzeFeatures 序列化时的特性定义,和常量一致

    private Integer id;

    @JSONField(name = "studentName",ordinal = 1)
    private String name;

    @JSONField(ordinal = 2)
    private Integer age;

    //@JSONField 注解属性:serialize 是否序列化该字段
    @JSONField(serialize = false)
    private String email;

    //@JSONField 注解属性:format 指定序列化后的格式
    @JSONField(format = "YYYY-MM-dd HH:mm:ss")
    private Date birthday;

    private Boolean flag;
}
3. 创建测试类:
import com.alibaba.fastjson.JSON;
import com.test.json.pojo.Student_JsonField;
import org.junit.Test;

import java.util.Date;

/**
 * @JSonField 注解
 * 该注解作用于方法上,字段上和方法的参数上。可在序列化和反序列化时进行特性功能定制
 */

public class TestFastJson3_JsonField {

    public Date getDate(){
        Date date = new Date();
        return date;
    }

    @Test
    public void testObjectToJson(){

        /**
         * @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
         * public @interface JSONField {
         *     int ordinal() default 0;
         *     注意看一下源代码
         */
        Student_JsonField student = new Student_JsonField();
        student.setId(1);
        student.setName("张三");
        student.setAge(20);
        student.setBirthday(getDate()); //转化毫秒值
        student.setEmail("zs@sina.com");

        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);

        // {"age":20,"birthday":1662565074308,"email":"zs@sina.com","id":1,"name":"张三"}

        //加完注解后 name
        // {"age":20,"birthday":1662565946059,"email":"zs@sina.com","id":1,"studentName":"张三"}

        //加完注解 ordinal
        //{"birthday":1662565915298,"email":"zs@sina.com","id":1,"studentName":"张三","age":20}

        //加完注解 format
        // {"birthday":"2022-09-07 23:54:03","email":"zs@sina.com","id":1,"studentName":"张三","age":20}

        //加完注解 serialize = false
        //{"birthday":"2022-09-07 23:55:21","id":1,"studentName":"张三","age":20}

    }
}
4.2.7 @JSONType注解
1. @JSONType源码

@JSONType该注解作用于类上对该类的字段进行序列化和反序列化的特性功能定制,该注解作用在类上

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.alibaba.fastjson.annotation;

import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface JSONType {
    boolean asm() default true;

    String[] orders() default {};

    String[] includes() default {};

    String[] ignores() default {};

    SerializerFeature[] serialzeFeatures() default {};

    Feature[] parseFeatures() default {};

    boolean alphabetic() default true;

    Class<?> mappingTo() default Void.class;

    Class<?> builder() default Void.class;

    String typeName() default "";

    String typeKey() default "";

    Class<?>[] seeAlso() default {};

    Class<?> serializer() default Void.class;

    Class<?> deserializer() default Void.class;

    boolean serializeEnumAsJavaBean() default false;

    PropertyNamingStrategy naming() default PropertyNamingStrategy.CamelCase;

    Class<? extends SerializeFilter>[] serialzeFilters() default {};
}
2. 创建实体类:
package com.test.json.pojo;

import com.alibaba.fastjson.annotation.JSONType;
import lombok.Data;

@Data
// 注解属性 includes 要被序列化的字段
// 注解属性 orders 序列化后的顺序
// 注解属性:serialzeFeatures 序列化时的特性定义和常数一致
@JSONType(includes = {"id","name","age","address"},orders = {"name","age","id","address"})
public class Student_JsonType {

    private int id;

    private String name;

    private int age;

    private String address;
}

3. 创建测试类
import com.alibaba.fastjson.JSON;
import com.test.json.pojo.Student_JsonType;
import org.junit.Test;

/**
 * @JSONType 注解
 * 该注解作用于类上,对该类的字段进行序列化和反序列化时的特性功能定制
 */
public class TestFastJson4_JsonType {

    @Test
    public void test(){

        Student_JsonType student = new Student_JsonType();
        student.setId(1);
        student.setAddress("北京市");
        student.setName("张三");
        student.setAge(22);

        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);

        //{"address":"北京市","age":22,"id":1,"name":"张三"}

        //如果去掉一个 @JSONType(includes = {"id","name","address"})
        // {"address":"北京市","id":1,"name":"张三"}


        /**
         * package com.test.json.fastjson.pojo;
         *
         * import com.alibaba.fastjson.annotation.JSONField;
         * import com.alibaba.fastjson.annotation.JSONType;
         * import lombok.Data;
         *
         * @Data
         * // 注解的属性 includes 要被序列化的字段
         * @JSONType(includes = {"id","name","address"})
         * public class Person {
         *
         *     private int id;
         *
         *     private String name;
         *
         *     @JSONField(serialize = true)  不管用
         *     private int age;
         *
         *     private String address;
         * }
         */
        //{"address":"北京市","id":1,"name":"张三"}

        //添加orders
        // @JSONType(includes = {"id","name","age","address"},orders = {"name","age","id","address"})
        //{"name":"张三","age":22,"id":1,"address":"北京市"}

    }
}
4.2.8 fastjson应用
package com.test.json.test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.test.json.pojo.Student;
import org.junit.Test;

import java.util.*;


public class TestFastJson1_Get {

    public Date getDate(){
        Date date = new Date();
        return date;
    }
 
    @Test
    public void Test() {
        //集合List,存储Student对象
        List<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("张三");
        student1.setAge(20);
        student1.setEmail("zs@sina.com");
        student1.setBirthday(getDate()); //转化毫秒值

        Student student2 = new Student();
        student2.setId(2);
        student2.setName("李四");
        student2.setAge(22);
        student2.setEmail("ls@126.com");
        student2.setBirthday(getDate()); //转化毫秒值
        //Student对象存储到List集合中
        list.add(student1);
        list.add(student2);
        //List集合,序列化为Json格式字符串
        String jsonString = JSON.toJSONString(list);
        System.out.println(list);
        // 转后的结果是数组,数组的元素是对象
        // [{"age":20,"birthday":1662539828574,"email":"zs@sina.com","id":1,"name":"张三"}, //数组是1对象:张三 2对象:李四
        // {"age":22,"birthday":1662539828574,"email":"ls@126.com","id":2,"name":"李四"}]

        //HashMap无序,LinkedHashMap按照插入顺序有序
        Map map = new LinkedHashMap<>();
        map.put("code",200);
        map.put("message","成功");
        map.put("data",list);

        //序列化,日期规范化、使用json格式输出
        String mapString = JSON.toJSONString(map, SerializerFeature.WriteDateUseDateFormat,SerializerFeature.PrettyFormat);
        System.out.println("mapString:"+mapString);
        System.out.println("=========================================");

        //反序列化
        JSONObject jsonObject = JSON.parseObject(mapString);

        Integer code = jsonObject.getInteger("code");
        System.out.println("code:"+code);

        String message = jsonObject.getString("message");
        System.out.println("message:"+message);
        System.out.println("--------------------------------------");

        JSONArray data = jsonObject.getJSONArray("data");

        for (int i = 0; i < data.size(); i++) {
            JSONObject dataString = data.getJSONObject(i);

            final String idString =dataString.getString("id");
            System.out.println("id:"+ idString );

            final String nameString =dataString.getString("name");
            System.out.println("name:"+ nameString );

            final String ageString =dataString.getString("age");
            System.out.println("age:"+ ageString );

            final String birthdayString =dataString.getString("birthday");
            System.out.println("birthday:"+ birthdayString );

            final String emailString =dataString.getString("email");
            System.out.println("email:"+ emailString );

            System.out.println("--------------------------------------");
        }

    }
}
4.2.8.1 结果:
[Student(id=1, name=张三, age=20, email=zs@sina.com, birthday=Fri Nov 18 23:43:06 CST 2022, flag=null), Student(id=2, name=李四, age=22, email=ls@126.com, birthday=Fri Nov 18 23:43:06 CST 2022, flag=null)]
mapString:{
	"code":200,
	"message":"成功",
	"data":[
		{
			"age":20,
			"birthday":"2022-11-18 23:43:06",
			"email":"zs@sina.com",
			"id":1,
			"name":"张三"
		},
		{
			"age":22,
			"birthday":"2022-11-18 23:43:06",
			"email":"ls@126.com",
			"id":2,
			"name":"李四"
		}
	]
}
=========================================
code:200
message:成功
--------------------------------------
id:1
name:张三
age:20
birthday:2022-11-18 23:43:06
email:zs@sina.com
--------------------------------------
id:2
name:李四
age:22
birthday:2022-11-18 23:43:06
email:ls@126.com
--------------------------------------

5. Jackson

  Jackson是用来序列化和反序列化 json 的 Java 的开源框架。Spring MVC 的默认 json 解析器便是 Jackson。与其他 Java 的 json 框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。
json

5.1 添加依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
</dependency>

 <!--对localDateTime等jdk8时间日期api的转化文件-->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.10.1</version>
</dependency>

5.1 序列化

5.1.1 创建实体类:
1. UserS类
package com.test.json.fastjson;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;


import java.time.LocalDateTime;
import java.util.Date;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserS{

    /**
     * 用户id
     */
    private Long id;

    /**
     * 用户姓名
     */
    private String name;

    /**
     * 密码
     */
    private String pwd;


    /**
     * 网站名1
     */
    private String websiteUrl;

    /**
     * 注册日期
     */
    private Date registerDate;


    /**
     * 生日
     */
    private LocalDateTime birthday;

    /**
     * 地址
     */
    private  String addr;
    
}
2. ResultDTO类: 用于后续泛型
package com.test.json.jackson;

import lombok.Data;

@Data
public class ResultDTO<T> {
    private Boolean success = Boolean.TRUE;
    private T data;
    private ResultDTO(){}

    public static <T> ResultDTO<T> buildSuccess(T t){
        ResultDTO<T> resultDTO = new ResultDTO<>();
        resultDTO.setData(t);
        return resultDTO;
    }

}
5.1.2 只包含非null属性
1. 全局配置

    //序列化
    private static ObjectMapper objectMapper = new ObjectMapper();
    static {
        /**
         * 序列化配置
         */
        //全局配置: 配置序列化时只包含非空属性
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
    /**
     * 序列化
     * 把bean转化成json字符串
     */
    @Test
    public void test1() throws JsonProcessingException {

        UserS user = new UserS();
        user.setId(1L);
        user.setName("张三");
        //user.setPwd("123");
        user.setAddr("河南");
        user.setWebsiteUrl("http://www.baidu.com");
        user.setRegisterDate(new Date());
        user.setBirthday(LocalDateTime.now());

        String string = objectMapper.writeValueAsString(user);
        System.out.println(string);

    }
2. 单个bean配置
@Data
@JsonInclude(JsonInclude.Include.NON_NULL) //类加上标签
public class UserS{
    ...
}
结果:
{"id":1,"name":"张三","websiteUrl":"http://www.baidu.com","registerDate":1661844024802,"birthday":{"month":"AUGUST","year":2022,"dayOfMonth":30,"hour":15,"minute":20,"monthValue":8,"nano":857000000,"second":24,"dayOfWeek":"TUESDAY","dayOfYear":242,"chronology":{"id":"ISO","calendarType":"iso8601"}},"addr":"河南"}
5.1.3 日期时间格式化
1. 全局配置
 static {
       /**
         * 全局的配置
         */
        //自动通过spi发现jackson的module并注册
        objectMapper.findAndRegisterModules();
 }
2.单个属性配置
    /**
     * 注册日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date registerDate;
结果:
{"id":1,"name":"张三","websiteUrl":"http://www.baidu.com","registerDate":"2022-08-30 15:42:10","birthday":"2022-08-30 15:42:10","addr":"河南"}
5.1.4 美化输出
static {
    //美化输出
    objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
}
结果:
{
  "id" : 1,
  "name" : "张三",
  "websiteUrl" : "http://www.baidu.com",
  "registerDate" : "2022-08-30 16:13:48",
  "birthday" : "2022-08-30 16:13:48",
  "addr" : "河南"
}

5.2 反序列化

5.2.1 忽略不存在的key

FastJson对于json中多的key默认的处理就是忽略!!!

添加这行代码,当遇到与类多余的属性时会选择不输出

static {
	   /**
         * 反序列化的配置
         */
        //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        //两种写法等价
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
    /**
     * 反序列化
     */
    @Test
    public void test2() throws Exception{
        String str = "{\"id\":1,\"name\":\"张三\",\"websiteUrl\":\"http://www.baidu.com\",\"registerDate\":\"2022-08-30 15:42:10\",\"birthday\":\"2022-08-30 15:42:10\",\"addr\":\"河南\"}\n";
        UserS user = objectMapper.readValue(str, UserS.class); //属性必须与类相同 多了少了 都会报错
        System.out.println(user); //Ctrl + Alt +V
    }

结果:

{
  "success" : true,
  "data" : {
    "name" : "张三",
    "websiteUrl" : "http://www.baidu.com"
  }
}
5.2.2 泛型处理
    /**
     * 泛型的处理
     */
    @Test
    public void test3() throws Exception{
        UserS user = new UserS();
        user.setName("张三");
        user.setWebsiteUrl("http://www.baidu.com");
        ResultDTO<User> userResultDTO = ResultDTO.buildSuccess(user);
        String dtoSerializationResult = objectMapper.writeValueAsString(userResultDTO);
        System.out.println(dtoSerializationResult);

        //反序列化为ResultDTO<User>
        ResultDTO<User> dataResult = objectMapper.readValue(dtoSerializationResult, new TypeReference<ResultDTO<User>>() {
        });
        System.out.println("dataResult:"+dataResult);
        System.out.println("data:"+dataResult.getData());
    }

结果:

{
  "success" : true,
  "data" : {
    "name" : "张三",
    "websiteUrl" : "http://www.baidu.com"
  }
}
dataResult:ResultDTO(success=true, data=User(id=null, name=张三, pwd=null, websiteUrl=http://www.baidu.com, registerDate=null, birthday=null, addr=null))
data:User(id=null, name=张三, pwd=null, websiteUrl=http://www.baidu.com, registerDate=null, birthday=null, addr=null)

5.3 通用配置

  • 序列化:驼峰下划线
  • 反序列化:下划线驼峰
static {       
    //驼峰命名法 转下划线
    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
}
  • 指定属性名和json字符串key的对应关系
    /**
     * 地址
     */
    @JsonProperty("address")
    private  String addr;
{
  "id" : 1,
  "name" : "张三",
  "pwd" : "123",
  "website_url" : "http://www.baidu.com",
  "register_date" : "2022-08-30 16:53:56",
  "birthday" : "2022-08-30 16:53:56",
  "address" : "河南"
}
  • 忽略指定属性
    /**
     * 密码
     */
    @JsonIgnore
    private String pwd;
{
  "id" : 1,
  "name" : "张三",
  "website_url" : "http://www.baidu.com",
  "register_date" : "2022-08-30 16:52:08",
  "birthday" : "2022-08-30 16:52:08",
  "address" : "河南"
}

5.4 其他应用

  • 对象更新
   /**
     * 对象更新,对象合并,如果后者属性有值,则用后者,否则前者的值不变
     */
    @Test
    public void test4() throws Exception {
        UserS orginalUser = new UserS();
        orginalUser.setId(2L);
        orginalUser.setName("张三");
        orginalUser.setWebsiteUrl("www.baidu.com");

        UserS newUser = new UserS();
        newUser.setId(3L);
        newUser.setWebsiteUrl("www.google.com");

        //让我们省去了很多判断
        UserS updateUser = objectMapper.updateValue(orginalUser, newUser);

        //id:2 name:张三 java websiteUrl:http:google.com
        System.out.println(updateUser);
    }

结果:

UserS(id=3, name=张三, pwd=null, websiteUrl=www.google.com, registerDate=null, birthday=null, addr=null)
  • 获取对象
   /**
     * 泛型的处理 获取具体值
     */
    @Test
    public void test5() throws Exception {
        UserS user = new UserS();
        user.setName("张三");
        user.setWebsiteUrl("http://www.baidu.com");


        UserS user2 = new UserS();
        user2.setName("李四");
        user2.setWebsiteUrl("http://www.google.com");

        List<UserS> list = new ArrayList<>();

        list.add(user);
        list.add(user2);
        ResultDTO<List<UserS>> userResultDTO = ResultDTO.buildSuccess(list);
        String dtoSerializationResult = objectMapper.writeValueAsString(userResultDTO);

        System.out.println(dtoSerializationResult);
        System.out.println("==============================================");


        //反序列化为ResultDTO<User>
        ResultDTO<List<UserS>> dataResult = objectMapper.readValue(dtoSerializationResult, new TypeReference<ResultDTO<List<UserS>>>() {
        });
        System.out.println(dataResult);
        System.out.println("==============================================");

        for (int i = 0; i < list.size(); i++) {
            System.out.println("data:" + dataResult.getData().get(i));
            System.out.println("name:"+dataResult.getData().get(i).getName());
            System.out.println("website_url:"+dataResult.getData().get(i).getWebsiteUrl());
            System.out.println("------------------------------------------------------------");
        }

结果:

{
  "success" : true,
  "data" : [ {
    "name" : "张三",
    "website_url" : "http://www.baidu.com"
  }, {
    "name" : "李四",
    "website_url" : "http://www.google.com"
  } ]
}
==============================================
ResultDTO(success=true, data=[UserS(id=null, name=张三, pwd=null, websiteUrl=http://www.baidu.com, registerDate=null, birthday=null, addr=null), UserS(id=null, name=李四, pwd=null, websiteUrl=http://www.google.com, registerDate=null, birthday=null, addr=null)])
==============================================
data:UserS(id=null, name=张三, pwd=null, websiteUrl=http://www.baidu.com, registerDate=null, birthday=null, addr=null)
name:张三
website_url:http://www.baidu.com
------------------------------------------------------------
data:UserS(id=null, name=李四, pwd=null, websiteUrl=http://www.google.com, registerDate=null, birthday=null, addr=null)
name:李四
website_url:http://www.google.com
------------------------------------------------------------

5.5 全部代码

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.test.json.jackson.ResultDTO;
import com.test.json.jackson.UserS;
import org.junit.Test;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class JacksonTest {

    //序列化
    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        /**
         * 序列化配置
         */
        //全局配置: 配置序列化时只包含非空属性
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //美化输出
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);

        /**
         * 反序列化的配置
         */
        //objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        //两种写法等价
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        /**
         * 通用的配置
         */
        //对Date进行配置,SimpleDateFormat是线程不安全的
        objectMapper.setDateFormat(new SimpleDateFormat(DATE_TIME_FORMAT));


        //自动通过spi发现jackson的module并注册
        //objectMapper.findAndRegisterModules(); //注释了 就可以用下面手动配置JavaModule

        //手动配置JavaTimeModule并注册
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //序列化
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
        //反序列化
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
        //注册:
        objectMapper.registerModule(javaTimeModule);

        //驼峰命名法 转下划线
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    }

    /**
     * 序列化
     * 把bean转化成json字符串
     */
    @Test
    public void test1() throws JsonProcessingException {

        UserS user = new UserS();
        user.setId(1L);
        user.setName("张三");
        user.setPwd("123");
        user.setAddr("河南");
        user.setWebsiteUrl("http://www.baidu.com");
        user.setRegisterDate(new Date());
        user.setBirthday(LocalDateTime.now());

        String string = objectMapper.writeValueAsString(user);
        System.out.println(string);

    }

    /**
     * 反序列化
     */
    @Test
    public void test2() throws Exception {
        String str = "{\"id\":1,\"name\":\"张三\",\"websiteUrl\":\"http://www.baidu.com\",\"registerDate\":\"2022-08-30 15:42:10\",\"birthday\":\"2022-08-30 15:42:10\",\"addr\":\"河南\"}\n";
        UserS user = objectMapper.readValue(str, UserS.class); //属性必须与类相同 多了少了 都会报错
        System.out.println(user); //Ctrl + Alt +V
    }

    /**
     * 泛型的处理
     */
    @Test
    public void test3() throws Exception {
        UserS user = new UserS();
        user.setName("张三");
        user.setWebsiteUrl("http://www.baidu.com");
        ResultDTO<UserS> userResultDTO = ResultDTO.buildSuccess(user);
        String dtoSerializationResult = objectMapper.writeValueAsString(userResultDTO);
        System.out.println(dtoSerializationResult);

        //反序列化为ResultDTO<User>
        ResultDTO<UserS> dataResult = objectMapper.readValue(dtoSerializationResult, new TypeReference<ResultDTO<UserS>>() {
        });
        System.out.println("dataResult:" + dataResult);
        System.out.println("data:" + dataResult.getData());
    }

    /**
     * 对象更新,对象合并,如果后者属性有值,则用后者,否则前者的值不变
     */
    @Test
    public void test4() throws Exception {
        UserS orginalUser = new UserS();
        orginalUser.setId(2L);
        orginalUser.setName("张三");
        orginalUser.setWebsiteUrl("www.baidu.com");

        UserS newUser = new UserS();
        newUser.setId(3L);
        newUser.setWebsiteUrl("www.google.com");

        //让我们省去了很多判断
        UserS updateUser = objectMapper.updateValue(orginalUser, newUser);

        //id:2 name:张三 java websiteUrl:http:google.com
        System.out.println(updateUser);
    }
   /**
     * 泛型的处理 获取具体值
     */
    @Test
    public void test5() throws Exception {
        UserS user = new UserS();
        user.setName("张三");
        user.setWebsiteUrl("http://www.baidu.com");


        UserS user2 = new UserS();
        user2.setName("李四");
        user2.setWebsiteUrl("http://www.google.com");

        List<UserS> list = new ArrayList<>();

        list.add(user);
        list.add(user2);
        ResultDTO<List<UserS>> userResultDTO = ResultDTO.buildSuccess(list);
        String dtoSerializationResult = objectMapper.writeValueAsString(userResultDTO);

        System.out.println(dtoSerializationResult);
        System.out.println("==============================================");


        //反序列化为ResultDTO<User>
        ResultDTO<List<UserS>> dataResult = objectMapper.readValue(dtoSerializationResult, new TypeReference<ResultDTO<List<UserS>>>() {
        });
        System.out.println(dataResult);
        System.out.println("==============================================");

        for (int i = 0; i < list.size(); i++) {
            System.out.println("data:" + dataResult.getData().get(i));
            System.out.println("name:"+dataResult.getData().get(i).getName());
            System.out.println("website_url:"+dataResult.getData().get(i).getWebsiteUrl());
            System.out.println("------------------------------------------------------------");
        }
}
JacksonFastjson都是Java中常用的JSON处理库,用于在Java对象和JSON字符串之间进行转换。它们的主要区别在于以下几个方面: 1. 性能:在性能方面,Fastjson通常被认为是更快的库。Fastjson具有较高的解析和序列化速度,特别是对于大型JSON数据或复杂对象的处理。Jackson也具有良好的性能,但在某些情况下,Fastjson可能会更快。 2. 注解支持:JacksonFastjson都支持使用注解来定制JSON序列化和反序列化的行为。Jackson使用`@JsonProperty`、`@JsonSerialize`等注解,而Fastjson使用`@JSONField`注解。两者的注解支持相似,但具体的注解名称和用法有所不同。 3. API设计:Jackson的API设计较为复杂,提供了更多的灵活性和可定制性。它使用了一系列的`ObjectMapper`、`JsonNode`等类来处理JSON数据。相比之下,Fastjson的API设计相对简单,使用起来更加直观和方便。 4. 第三方框架集成:由于JacksonJava领域中使用最广泛的JSON库之一,许多框架和库都对其进行了集成支持。在某些情况下,一些第三方库可能更喜欢或更适配于JacksonFastjson在这方面的支持相对较少,但也有一些框架对其提供了支持。 总的来说,JacksonFastjson都是优秀的JSON处理库,选择哪个库取决于项目的具体需求和性能要求。如果对性能要求较高且需要更多的定制化能力,可以考虑使用Fastjson。如果项目已经集成了许多基于Jackson的库或框架,或者需要更大的生态系统支持,那么使用Jackson可能更为方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值