Hefery 的个人网站

Hefery's Personal Website

Contact:hefery@126.com
  menu
73 文章
0 浏览
2 当前访客
ღゝ◡╹)ノ❤️

JSON数据格式和FastJson的使用

JSON

JSON介绍

JSON:JavaScript Object Notation,JavaScript 对象表示法,一种轻量级的文本数据交换格式,采用一种“key:value”的文本格式来存储和交换文本信息的语法,类似 XML

JSON 通常用于前端与后端交换数据

JSON 文件:

  • JSON 文件的文件类型是 .json
  • JSON 文本的 MIME 类型是 application/json

特点:

  • JSON 比 XML 更小、更快,更易解析

JSON在线视图查看器:JSON在线视图查看器

JSON和XML区别
JSON与XML相同:纯文本;层级结构(值中存在值);通过JavaScript解析;数据可使用AJAX进行传输
JSON与XML不同:没有结束标签;更短,数据读写的速度更快;可以使用数组

最大的不同是:XML 需要使用 XML 解析器来解析,JSON 可以使用标准的 JavaScript 函数来解析

  • JSON.parse(): 将一个 JSON 字符串转换为 JavaScript 对象
  • JSON.stringify(): 于将 JavaScript 值转换为 JSON 字符串
# JSON实例
{
    "sites": [
        { "name":"hefery" , "age":23 }, 
        { "name":"lalala" , "age":24 }, 
        { "name":"hahaha" , "age":25 }
    ]
}

# XML实例
<sites>
  <site>
    <name>hefery</name> <age>23</age>
  </site>
  <site>
    <name>lalala</name> <age>24</age>
  </site>
  <site>
    <name>hahaha</name> <age>25</age>
  </site>
</sites>

为什么使用 JSON?
对于 AJAX 应用程序来说,JSON 比 XML 更快更易使用:

  • 使用 XML
    读取 XML 文档
    使用 XML DOM 来循环遍历文档
    读取值并存储在变量中
  • 使用 JSON
    读取 JSON 字符串
    用 eval() 处理 JSON 字符串

JSON语法

数据格式:key:value,"name" : "Hefery"

语法规则:

  • key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)
  • key 和 value 中使用冒号“:”分隔
  • 每个 key/value 对使用逗号“,”分隔
  • 大括号 {} 保存对象
  • 中括号 [] 保存数组,数组可以包含多个对象

Json 值:

  • 数字(整数或浮点数):{ "age":30 }
  • 字符串(在双引号中)
  • 逻辑值(true 或 false):{ "flag":true }
  • 数组(中括号中)
    [
        { key1 : value1-1 , key2:value1-2 }, 
        { key1 : value2-1 , key2:value2-2 }, 
        { key1 : value3-1 , key2:value3-2 }, 
        ...
        { key1 : valueN-1 , key2:valueN-2 }, 
    ]
    
    // 对象 sites 是包含三个对象的数组
    {
        "sites": [
            { "name":"hefery" , "age":23 }, 
            { "name":"lalala" , "age":24 }, 
            { "name":"hahaha" , "age":25 }
        ]
    }
    
  • 对象(大括号中):{key1:value1, key2:value2, ... keyN:valueN } site={ "name":"hefery" , "age":23 }
    访问或修改值:site["name"]; 或 site.name
  • null:{ "runoob":null }

嵌套 JSON 对象或对象数组

# JSON对象
cart= {
    "name":"旺仔",
    "price":10000,
    "items": {
        "item1":"旺仔牛奶糖",
        "item2":"旺仔QQ糖",
        "item3":"旺仔小馒头"
    }
}
# 访问或修改元素
cart.items[1]  # 旺仔牛奶糖

# JSON对象数组
cart= {
    "name":"旺仔",
    "num":3,
    "items": [
        { "name":"旺仔牛奶糖", "info":[ "旺仔", "牛奶糖" ] },
        { "name":"旺仔QQ糖",   "info":[ "旺仔", "QQ糖" ] },
        { "name":"旺仔小馒头", "info":[ "旺仔", "小馒头" ] }
    ]
}
# 访问或修改元素
cart.items[1].name  # 旺仔牛奶糖

JSON应用

前端

JSON 使用 JavaScript 语法,所以无需额外的软件就能处理 JavaScript 中的 JSON

# 创建一个 JSON 对象sites
var sites = [
    { "name":"hefery" , "age":23 },
    { "name":"lalala" , "age":24 }, 
    { "name":"hahaha" , "age":25 }
];
# 访问数组对象sites的元素(索引从 0 开始)
sites[0].name;  # hefery,通过索引下标访问
# 修改数组对象sites的元素(索引从 0 开始)
sites[0].name="hebin";


# 创建一个 JSON 对象数组cart
var cart = {
    "name":"旺仔",
    "num":3,
    "items": [
        { "name":"旺仔牛奶糖", "info":[ "旺仔", "牛奶糖" ] },
        { "name":"旺仔QQ糖",   "info":[ "旺仔", "QQ糖" ] },
        { "name":"旺仔小馒头", "info":[ "旺仔", "小馒头" ] }
    ]
};
# 访问或修改元素
cart.items[1].name  # 旺仔牛奶糖

JSON文本转JavaScript对象

JSON.parse(): var obj = JSON.parse('{ "name":"hefery", "age":23, "site":"www.hefery.icu" }');

JSON.parse(text[, reviver])
text:必输, 一个有效的 JSON 字符串
reviver:可选,一个转换结果的函数, 将为对象的每个成员调用此函数

在线检测 JSON 格式工具:在线检测 JSON 格式工具

JSON格式化校验:JSON格式化校验

注意:JSON 不能存储 Date 对象,需要将其转换为字符串

var text = { "name":"hefery", "age":23, "initDate":new Date(), "site":"www.hefery.icu" };
var obj = JSON.parse(text, function(key, value) {
    if (key == "initDate") {
        return new Date(value);
    } else {
        return value;
    }
});

JavaScript对象转JSON文本

JSON.stringify():

JSON.stringify(value[, replacer[, space]])

  • value:必输, 要转换的 JavaScript 值(通常为对象或数组)
  • replacer:可选。用于转换结果的函数或数组。
    • 如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""
    • 如果 replacer 为数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。当 value 参数也为数组时,将忽略 replacer 数组
  • space::可选,文本添加缩进、空格和换行符
    • 如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格
    • space 也可以使用非数字,如:\t
var text = { "name":"hefery", "age":23, "initDate":"2022-01-14", "site":"www.hefery.icu" };
var obj = JSON.stringify(text);

注意:JSON 不能存储 Date 对象,需要将其转换为字符串

var text = { "name":"hefery", "age":23, "initDate":new Date(), "site":"www.hefery.icu" };
# 在执行 JSON.stringify() 函数前将函数转换为字符串
text.initDate = text.initDate.toString();
var obj = JSON.stringify(text);

后端

FastJson

数据准备:

  • POJO

    • User:用户
      Integer id;  
      String uaername;  
      List<IdCard> idCards = new ArrayList<IdCard>();
      
    • IdCard:银行卡
      String cardNo;
      String sex;
      Date birthday;
      BigDecimal balance;
      
  • Test

    public class FastJsonTest {
    
        /** JSON对象 */
        private User user;
        /** JSON对象数组 */
        private List<User> userList = new ArrayList<User>();
    
        /** 序列化后的JSON对象字符串 */
        private String userStr;
        /** 序列化后的JSON对象数组字符串 */
        private String userListStr;
    
        /** 为序列化做准备 */
        @Before
        public void initObjectAndList() {
            // 银行卡List1
            List<IdCard> idCardList1 = new ArrayList<IdCard>();
            IdCard idCard1 = new IdCard("10001", "男", new Date(), new BigDecimal("666.666"));
            IdCard idCard2 = new IdCard("10002", "男", new Date(), new BigDecimal("777.777"));
            idCardList1.add(idCard1);
            idCardList1.add(idCard2);
            // user用户有两张银行卡 idCard1 + idCard2
            user = new User(1, "Hefery", idCardList1);
    
            // 银行卡List2
            List<IdCard> idCardList2 = new ArrayList<IdCard>();
            IdCard idCard3 = new IdCard("10003", "男", new Date(), new BigDecimal("888.888"));
            IdCard idCard4 = new IdCard("10004", "男", new Date(), new BigDecimal("999.999"));
            idCardList2.add(idCard3);
            idCardList2.add(idCard4);
            // hahaha用户有两张银行卡 idCard3 + idCard4
            User hahaha = new User(2, "hahaha", idCardList2);
    
            userList.add(user);
            userList.add(hahaha);
        }
    
        /** 为反序列化做准备 */
        @Before
        public void initString() {
            userStr = "{\"id\":1,\"idCards\":[{\"balance\":666.666,\"birthday\":1646565945957,\"cardNo\":\"10001\",\"sex\":\"男\"},{\"balance\":777.777,\"birthday\":1646565945958,\"cardNo\":\"10002\",\"sex\":\"男\"}],\"uaername\":\"Hefery\"}";
            userListStr = "[{\"id\":1,\"idCards\":[{\"balance\":666.666,\"birthday\":1646565945957,\"cardNo\":\"10001\",\"sex\":\"男\"},{\"balance\":777.777,\"birthday\":1646565945958,\"cardNo\":\"10002\",\"sex\":\"男\"}],\"uaername\":\"Hefery\"},{\"id\":2,\"idCards\":[{\"balance\":888.888,\"birthday\":1646565945958,\"cardNo\":\"10003\",\"sex\":\"男\"},{\"balance\":999.999,\"birthday\":1646565945958,\"cardNo\":\"10004\",\"sex\":\"男\"}],\"uaername\":\"hahaha\"}]";
        }
    
    }
    
FastJson使用

引入依赖

<!-- Fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
</dependency>
FastJson序列化

JSON序列化:JSON对象 --> 字符串

String userString = JSON.toJSONString(user);
String userListString = JSON.toJSONString(userList);

public class FastJsonTest {

    /**
     * 序列化JSON:JSON文本 --> 字符串
     */
    @Test
    public void test01() {
        String userString = JSON.toJSONString(user);
        String userListString = JSON.toJSONString(userList);
	// 打印 JSON 对象 
        System.out.println(userString);
	// 打印 JSON 对象数组
        System.out.println(userListString);
    }

    /**
     * 序列化JSON:JSON文本 --> OutputStream
     */
    @Test
    public void test09() throws IOException {
        File file = new File("D:\\data\\test.json");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        JSON.writeJSONString(fileOutputStream, user);
    }
 
}

// JSON 对象
{
  "id": 1,
  "idCards": [
    {
      "balance": 666.666,
      "birthday": 1646565945957,
      "cardNo": "10001",
      "sex": "男"
    },
    {
      "balance": 777.777,
      "birthday": 1646565945958,
      "cardNo": "10002",
      "sex": "男"
    }
  ],
  "uaername": "Hefery"
}

// JSON 对象数组
[
  {
    "id": 1,
    "idCards": [
      {
        "balance": 666.666,
        "birthday": 1646565945957,
        "cardNo": "10001",
        "sex": "男"
      },
      {
        "balance": 777.777,
        "birthday": 1646565945958,
        "cardNo": "10002",
        "sex": "男"
      }
    ],
    "uaername": "Hefery"
  },
  {
    "id": 2,
    "idCards": [
      {
        "balance": 888.888,
        "birthday": 1646565945958,
        "cardNo": "10003",
        "sex": "男"
      },
      {
        "balance": 999.999,
        "birthday": 1646565945958,
        "cardNo": "10004",
        "sex": "男"
      }
    ],
    "uaername": "hahaha"
  }
]

注意:日期格式是毫秒值

FastJson反序列化

反序列化JSON:字符串 --> JSON文本

User user = JSON.parseObject(userStr, User.class);

List<User> userList = JSON.parseArray(userListStr, User.class);

public class FastJsonTest {
    /**
     * 反序列化JSON:字符串 --> JSON文本
     */
    @Test
    public void test02() {
        User user = JSON.parseObject(userStr, User.class);
        List<User> userList = JSON.parseArray(userListStr, User.class);
        System.out.println(user);
        System.out.println(userList);
    }

    /**
     * 反序列化JSON:request对象的InputStream流的数据 --> JSON文本
     */
    @Test
    public void test03() throws IOException {
        InputStream inputStream1 = new ByteArrayInputStream(userStr.getBytes());
        Map<String, User> mapObject = JSON.parseObject(inputStream1, Map.class);
        System.out.println(mapObject);
    }

}

// user
User(id=1, uaername=Hefery, idCards=[IdCard(cardNo=10001, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=666.666), IdCard(cardNo=10002, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=777.777)])

// userList
[User(id=1, uaername=Hefery, idCards=[IdCard(cardNo=10001, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=666.666), IdCard(cardNo=10002, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=777.777)]), User(id=2, uaername=hahaha, idCards=[IdCard(cardNo=10003, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=888.888), IdCard(cardNo=10004, sex=男, birthday=Sun Mar 06 19:25:45 GMT+08:00 2022, balance=999.999)])]
FastJson需求
所有响应给前端的数据都只保留两位小数

坑:配置 SerializeConfig 全局实现 BigDecimal 类型保留两位小数,如果 POJO 类的 BigDecimal 类型字段没有添加 @JSONField(format = "") 注解并且没有(只要有一个,无论加在哪个字段),SerializeConfig 配置不会生效。

  • 序列化配置时,设置转换规则
    // 序列化配置:能够制定具体类型使用的序列转换器
    SerializeConfig serializeConfig = new SerializeConfig();
    serializeConfig.put(BigDecimal.class, new ObjectSerializer() {
        public final Integer newScale = 2;
        @Override
        public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
            if (o == null) {
                jsonSerializer.out.write("0.00");
                return;
            }
            jsonSerializer.out.write(((BigDecimal) o).setScale(newScale, RoundingMode.DOWN).toString());
                }
         });  // BigDecimal 类型保留两位小数*/
    serializeConfig.put(BigDecimal.class, new DIYBigDecimalSerializer(2));  //BigDecimal保留两位小数
    fastJsonConfig.setSerializeConfig(serializeConfig);
    
  • 通过过滤器,PropertyFilter不序列化 BigDecimal 类型,BeforeFilter/AfterFilter 手动填充
    PropertyFilter propertyFilter = new PropertyFilter() {
        @Override
        public boolean apply(Object object, String name, Object value) {
            // PropertyFilter不序列化 BigDecimal 类型,BeforeFilter/AfterFilter 手动填充
            if (value instanceof BigDecimal) {
                return false;
            }
            return true;
         }
    };
    
    AfterFilter afterFilter = new AfterFilter() {
        @Override
        public void writeAfter(Object object) {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() == BigDecimal.class) {
                    field.setAccessible(true);
                    Object value = null;
                    try {
                        value = ((BigDecimal)field.get(object)).setScale(2, RoundingMode.DOWN);
                    } catch (IllegalAccessException e) {
                         e.printStackTrace();
                    }
                    writeKeyValue(field.getName(), value);
                }
    
            }
        }
    };
    fastJsonConfig.setSerializeFilters(propertyFilter, afterFilter);
    
  • 通过过滤器,ValueFilter 设置 BigDecimal 类型序列化时将值保留两位小数
    ValueFilter valueFilter = new ValueFilter() {
        @Override
        public Object process(Object object, String name, Object value) {
            if (value instanceof BigDecimal) {
                value = ((BigDecimal)value).setScale(1, RoundingMode.DOWN);
            }
            return value;
        }
    };
    fastJsonConfig.setSerializeFilters(valueFilter);
    
所有日期格式转换为“yyy-MM-dd HH🇲🇲ss”格式
  • 在POJO类的日期字段添加注解 @JSONField(format = "yyyy-MM-dd HH:mm:ss")

    @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 */
        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 "";
    }
    
  • SerializerFeature.WriteDateUseDateFormat:

    @Test
    public void test04() {
        // 指定日期格式
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        String userString = JSON.toJSONString(user, SerializerFeature.WriteDateUseDateFormat);
        String userListString = JSON.toJSONString(userList, SerializerFeature.WriteDateUseDateFormat);
        System.out.println(userString);
        System.out.println(userListString);
    }
    
  • SerializeConfig:

    @Test
    public void test04() {
            SerializeConfig config = new SerializeConfig();
            config.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
            String userString = JSON.toJSONString(user, config);
            String userListString = JSON.toJSONString(userList, config);
            System.out.println(userString);
            System.out.println(userListString);
    }
    
  • ValueFilter

    public void test04() {
            // @JSONField注解会失效
            ValueFilter valueFilter = new ValueFilter() {
                @Override
                public Object process(Object object, String name, Object value) {
                    if (value instanceof Date) {
                        value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
                    }
                    return value;
                }
            };
    
            String userString = JSON.toJSONString(user, valueFilter);
            String userListString = JSON.toJSONString(userList, valueFilter);
            System.out.println(userString);
            System.out.println(userListString);
    }
    
消除对同一对象重复引用问题—$ref

fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);

FastJson转换器配置
@Configuration
public class FastJsonConverConfig {

    @Bean
    public HttpMessageConverters httpMessageConverters() {
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();

        /** 设置支持的 MediaType */
        List<MediaType> typeList = new ArrayList<MediaType>();
        typeList.add(MediaType.APPLICATION_JSON_UTF8);  // public static final MediaType APPLICATION_JSON = new MediaType("application", "json");
        fastJsonHttpMessageConverter.setSupportedMediaTypes(typeList);

        /** 设置 FastJson 全局配置(注意:注解失效) */
        FastJsonConfig fastJsonConfig = new FastJsonConfig();

        // 编码格式
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);

        // 序列化格式处理
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.QuoteFieldNames,  // 输出 key 时是否使用双引号,默认为 true
                //SerializerFeature.UseSingleQuotes,  // 使用单引号而不使用双引号,默认为 false
                SerializerFeature.WriteMapNullValue, // 是否输出为null的字段,默认为 false
                //SerializerFeature.WriteEnumUsingToString,  // Enum枚举输出 name() 或 original,默认为 false
                //SerializerFeature.WriteEnumUsingName,  // 用 Enum枚举 nmae() 输出
                //SerializerFeature.UseISO8601DateFormat, // Date 使用 ISO8601 格式输出,默认为 false
                SerializerFeature.WriteNullListAsEmpty, // List 字段如果为 null,输出 []
                SerializerFeature.WriteNullStringAsEmpty, // 字符类型字段如果为 null,输出 ""
                SerializerFeature.WriteNullNumberAsZero, // 数值类型字段如果为 null,输出 []
                SerializerFeature.WriteNullBooleanAsFalse, // 布尔类型字段如果为 null,输出 false
                //SerializerFeature.SkipTransientField, // 如果为 true,类中的 getter 方法对应的 Field 是 Transient,序列化时会被忽略,默认为 true
                //SerializerFeature.SortField, // 按字段名称排序后输出,默认为 false
                //SerializerFeature.PrettyFormat, // 结果是否格式化,默认为 false
                //SerializerFeature.WriteClassName, // 序列化时写入类型信息,默认为 false
                SerializerFeature.DisableCircularReferenceDetect  // 消除对同一对线循环引用的问题
                //SerializerFeature.WriteSlashAsSpecial, // 对斜杠“/”进行转义
                //SerializerFeature.WriteDateUseDateFormat  // 全局修改日期格式,默认为 false
        );

        // 序列化配置:能够制定具体类型使用的序列转换器
        SerializeConfig serializeConfig = new SerializeConfig();
        serializeConfig.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));  // 日期的转换格式
        //serializeConfig.put(String.class, PropertyNamingStrategy.CamelCase);  // 设置字段的命名规则:驼峰 userid --> userId
        /*serializeConfig.put(BigDecimal.class, new ObjectSerializer() {  // BigDecimal 类型保留两位小数
            public final Integer newScale = 2;
            @Override
            public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
                if (o == null) {
                    jsonSerializer.out.write("0.00");
                    return;
                }
                jsonSerializer.out.write(((BigDecimal) o).setScale(newScale, RoundingMode.DOWN).toString());
            }
        });*/
        //serializeConfig.put(BigDecimal.class, new DIYBigDecimalSerializer(2));  // BigDecimal 类型保留两位小数
        fastJsonConfig.setSerializeConfig(serializeConfig);

        // 反序列化配置:不常用
        //fastJsonConfig.setParserConfig();

        /**
         * 序列化过滤器:
         * 执行顺序:PropertyPreFilter -> PropertyFilter -> NameFilter -> ValueFiter -> BeforeFilter -> AfterFilter
         *      PropertyPreFilter:根据PropertyName判断是否序列化
         *      PropertyFilter:在序列化,设定那些字段是否被序列化
         *      NameFilter:序列化时修改Key的名称,比如属性名为name,可以修改为Name
         *      ValueFilter:序列化时修改Value
         *      BeforeFilter:在序列化对象的所有属性之前执行某些操作
         *      AfterFilter:在序列化对象的所有属性之后执行某些操作
         */
        fastJsonConfig.setSerializeFilters();

        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
        return new HttpMessageConverters(fastJsonHttpMessageConverter);
    }

}

Gson


标题:JSON数据格式和FastJson的使用
作者:Hefery
地址:http://hefery.icu/articles/2022/03/06/1646581369230.html