2021.11.2:使用JSON
JSON是JavaScript Object Notation的缩写,它去除了所有JavaScript执行代码,只保留了JavaScript的对象格式。一个典型的JSON如下:
{ "id": 1, "name": "Java核心技术", "author": { "firstName": "Abc", "lastName": "Xyz" }, "isbn": "1234567", "tags": ["Java", "Network"] }
JSON作为数据传输的格式,有几个显著的优点:
- JSON只允许UTF-8编码,不存在编码问题;
- JSON只允许使用双引号作为Key,特殊字符用\转义,格式简单;
- 浏览器内置JSON支持,如果把数据用JSON发送给浏览器,可以用JavaScript直接处理。
因此,JSON适合表示层次结构,因为它格式简单,只支持以下几种数据类型:
- 键值对:{"Key":Value}
- 数组:[1,2,3]
- 字符串:"abc"
- 数值(整数和浮点数):12.34
- 布尔值:true或false
- 空值:null
浏览器直接支持使用JavaScript对JSON进行读写:
//JSON string → JavaScript object jsObj = JSON.parse(jsonstr); //JavaScript object → JSON string jsonStr = JSON.stringify(jsObj);
所以开发Web应用时,使用JSON作为数据传输,在浏览器端非常方便。因为JSON天生适合JavaScript处理,所以,绝大多数REST API都选择JSON作为数据传输格式。
但是,如何使用Java对JSON进行读写?
在Java中,如果我们能直接在JSON和JavaBean之间转换,那么用起来就简单多了。
常用的解析JSON的第三方库有:
- Jackson
- Gson
- Fastjson
- ...
在使用Jackson进行解析JSON时,我们需要引入以下Maven依赖:
- com.fasterxml.jackson.core:jackson-databind:2.10.0
就可以使用下面的代码解析一个JSON文件:
InputStream input = Main.class.getResourceAsStream("/book.json"); ObjectMapper mapper = new ObjectMapper(); //反序列化时忽略不存在的JavaBean属性: mapper.configure(DeserializationFeature.FAIL_ON_UNKONWN_PROPERTIES,false); Book book = mapper.readValue(input,Book.class);
核心代码是创建一个ObjectMapper对象。关闭DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES功能使得解析时如果JavaBean不存在该属性时解析也不会报错。
把JSON解析为JavaBean的过程称为反序列化。如果把JavaBean变为JSON,那就是序列化。要实现JavaBean到JSON的序列化,只需要一行代码:
String json = mapper.writeValueAsString(book);
要把JSON中的某些值解析为特定的Java对象,例如LocalDate,也是完全可以的,例如:
{ "name":"Java核心技术", "pubDate":"2016-09-01" }
要解析为:
public class Book{ public String name; public LocalDate pubDate; }
只需要引入标准的JSR 310关于JavaTime的数据格式定义至Maven:
- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.0
然后,在创建ObjectMapper时,注册一个新的JavaTimeModule:
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
有时,内置的解析规则和扩展的解析规则如果都不满足我们的需求,我们还可以自定义解析。
例如,假设Book类的isbn是一个BigInteger:
public class Book{ public String name; public BigInteger isbn; }
但是JSON数据并不是标准的整型格式:
{ "name":"Java核心技术", "isbn":"978-7-111-54742-6" }
直接解析,肯定报错。这时,我们需要自定义一个IsbnDeserializer,用于解析含有非数字的字符串:
public class IsbnDeserializer extends JsonDeserializer<BigInteger>{ public BigInteger deserialize(JsonParser p,DeserializationContext ctxt) throws IOException,JsonProcessingException{ //读取原始JSON字符串内容g: String s = p.getValueAsString(); if(s!=null){ try{ return new BigInteger(s.replace("-","")); }catch(NumberFormatException e){ throw new JsonParseException(p,s,e); } } return null; } }
然后,在Book类中使用注解标注:
public class Book{ public String name; //表示反序列化isbn时使用自定义的IsbnDeserializer: @JsonDeserialize(using = IsbnDeserializer.class) public BigInteger isbn; }
类似的,自定义序列化时我们需要自定义一个IsbnSerializer,然后在Book类中标注@JsonSerialize(using = ...)即可。
小结
Jackson可以实现JavaBean和JSON之间的转换;
可以通过Module扩展Jackson能处理的数据类型;
可以自定义JsonSerializer和JsonDeserializer来定制序列化和反序列化。

浙公网安备 33010602011771号