一、概念
1)、定义
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
2)、作用
数据标记,存储,传输
3)、特点
- 1、读写速度快
- 2、解析简单
- 3、轻量级
- 4、独立于语言,平台
- 5、具有自我描叙性
4)、JSON解析图
二、语法
JSON建构于两种结构:
- “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
- 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
JSON具有以下这些形式:
1)、object
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。
{
"name":"语文",
"score":98
}
2)、array
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。
{
"courses":[
{
"name":"语文",
"score":98
},
{
"name":"英语",
"score":97.4
}
]
}
3)、value
值(value)可以是双引号括起来的字符串(string)、数值(number)、true 、false 、 null 、对象(object)或者数组(array)。这些结构可以嵌套。
{
"url":"[https://qqe2.com](https://qqe2.com/)",
"name":"欢迎使用JSON在线解析编辑器",
"array":{
"JSON校验":"[http://jsonlint.qqe2.com/](http://jsonlint.qqe2.com/)",
"Cron生成":"[http://cron.qqe2.com/](http://cron.qqe2.com/)",
"JS加密解密":"[http://edit.qqe2.com/](http://edit.qqe2.com/)"
},
"boolean":true,
"null":null,
"number":123,
"object":{
"a":"b",
"c":"d",
"e":"f"
}
}
4)、string
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
字符串(string)与C或者Java的字符串非常相似。
{
"name":"Zero"
}
5)、number
数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
{
"age": 28
}
三、JSON解析方式
1)、Android Studio自带org.json解析
解析原理:基于文档驱动,需要把全部文件读入到内存中,然后遍历所有数据,根据需要检索想要的数据。
生成JSON
private void createJson(Context context) throws Exception {
//获取到应用在内部的私有文件夹下对应的orgjson.json文件
File file = new File(getFilesDir(), "orgjson.json");
JSONObject student = new JSONObject();//实例化一个JSONObject对象
student.put("name", "OrgJson");//对其添加一个数据
student.put("sax", "男");
student.put("age", 23);
JSONObject course1 = new JSONObject();
course1.put("name", "语文");
course1.put("score", 98.2f);
JSONObject course2 = new JSONObject();
course2.put("name", "数学");
course2.put("score", 93.2f);
JSONArray coures = new JSONArray();//实例化一个JSON数组
coures.put(0, course1);//将course1添加到JSONArray,下标为0
coures.put(1, course2);
//然后将JSONArray添加到名为student的JSONObject
student.put("courses", coures);
FileOutputStream fos = new FileOutputStream(file);
fos.write(student.toString().getBytes());
fos.close();
Log.i(TAG, "createJson: " + student.toString());
Toast.makeText(context, "创建成功", Toast.LENGTH_LONG).show();
}
解析JSON
private void parseJson(Context context) throws Exception {
File file = new File(getFilesDir(), "orgjson.json");
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line;
StringBuffer sb = new StringBuffer();
while (null != (line = br.readLine())) {
sb.append(line);
}
fis.close();
isr.close();
br.close();
Student student = new Student();
//利用JSONObject进行解析
JSONObject stuJsonObject = new JSONObject(sb.toString());
//为什么不用getString?
//optString会在得不到你想要的值时候返回空字符串"",而getString会抛出异常
String name = stuJsonObject.optString("name", "");
student.setName(name);
student.setSax(stuJsonObject.optString("sax", "男"));
student.setAge(stuJsonObject.optInt("age", 18));
//获取数组数据
JSONArray couresJson = stuJsonObject.optJSONArray("courses");
for (int i = 0; i < couresJson.length(); i++) {
JSONObject courseJsonObject = couresJson.getJSONObject(i);
Course course = new Course();
course.setName(courseJsonObject.optString("name", ""));
course.setScore((float) courseJsonObject.optDouble("score", 0));
student.addCourse(course);
}
Log.i(TAG, "parseJson: " + student);
Toast.makeText(context, "解析成功", Toast.LENGTH_LONG).show();
}
2)、Gson 解析
- 解析原理:基于事件驱动
- 解析流程:根据所需取的数据 建立1个对应于JSON数据的JavaBean类,即可通过简单操作解析出所需数据
- Gson 不要求JavaBean类里面的属性一定全部和JSON数据里的所有key相同,可以按需取数据
具体实现
- 创建一个与JSON数据对应的JavaBean类(用作存储需要解析的数据)
- JSON的大括号对应一个对象,对象里面有key,value;JavaBean的类属性名 = key
- JSON的方括号对应一个数组, JavaBean里面对应的也是数组
- 对象里 可以有值/对象
- 若对象里面只有值,没有key,则说明是纯数组,对应JavaBean里的数组类型
- 若对象里面有值和key,则说明是对象数组,对应JavaBean里的内部类
- 对象嵌套:建立内部类 该内部类对象的名字 = 父对象的key ,类似对象数组
{
"key":"value",
"simpleArray":[
1,
2,
3
],
"arrays":[
[
{
"arrInnerClsKey":"arrInnerClsValue",
"arrInnerClsKeyNub":1
}
]
],
"innerclass":{
"name":"zero",
"age":25,
"sax":"男"
}
}
转化成JavaBean
public class GsonBean {
private String key;
private List<Integer> simpleArray;
private List<List<ArraysDTO>> arrays;
private InnerclassDTO innerclass;
public void setInnerclass(InnerclassDTO innerclass) {
this.innerclass = innerclass;
}
public static class InnerclassDTO {
private String name;
private Integer age;
private String sax;
}
public static class ArraysDTO {
private String arrInnerClsKey;
private Integer arrInnerClsKeyNub;
}
}
注意:bean文件中省略get/set方法
使用例子
public static void main(String... args) throws Exception {
Student student = new Student();
student.setName("Zero");
student.setSax("男");
student.setAge(28);
student.addCourse(new Course("英语", 78.3f));
Gson gson = new Gson();
//1. 生成json文件
File file = new File(CurPath + "/gsonjsontest.json");
OutputStream oot = new FileOutputStream(file);
JsonWriter jw = new JsonWriter(new OutputStreamWriter(oot, "utf- 8"));
gson.toJson(student, new TypeToken<Student>() {}.getType(), jw);
jw.flush();
jw.close();
//反序列化
Student student1 = gson.fromJson(new JsonReader(new
InputStreamReader(new FileInputStream(file)))
, new TypeToken<Student>() {
}.getType());
System.out.println(student1);
}
3)、Jackson解析
- 解析原理:基于事件驱动
- 解析过程:
- 类似 GSON,先创建1个对应于JSON数据的JavaBean类,再通过简单操作即可解析。
- 与 Gson解析不同的是:GSON可按需解析,即创建的JavaBean类不一定完全涵盖所要解析的JSON数据,按需创建属性;但Jackson解析对应的JavaBean必须把Json数据里面的所有key都有所对应,即必须把JSON内的数据所有解析出来,无法按需解析。
导入Jackson依赖
//如何配置jackson https://mvnrepository.com/search?q=jackson
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
使用Jackson解析
public static void main(String[] args) {
Student student = new Student();
student.setName("杰克逊");
student.setSax("男");
student.setAge(28);
student.addCourse(new Course("英语", 78.3f));
student.addCourse(new Course("语文", 88.9f));
student.addCourse(new Course("数学", 48.2f));
ObjectMapper objectMapper = new ObjectMapper();
//jackson序列化
File file = new File(CurPath + "/jacksontest.json");
FileOutputStream fileOutputStream = new FileOutputStream(file);
objectMapper.writeValue(fileOutputStream, student);
//反序列化
Student student1 = objectMapper.readValue(file, Student.class);
System.out.println(student1);
HashMap<String, Student> studentHashMap = new HashMap<>();
Student stu1 = new Student("King", "男", 32);
stu1.addCourse(new Course("物理", 68.9f));
Student stu2 = new Student("Mark", "男", 33);
studentHashMap.put("key1", stu1);
studentHashMap.put("key2", stu2);
System.out.println("studentHashMap:\n" + studentHashMap);
JacksonUtil.encode2File(studentHashMap, CurPath + "/jacksontest1.json");
String jsonStr = JacksonUtil.encode(studentHashMap);
System.out.println(jsonStr);
//正确的方式
HashMap<String, Student> studentHashMap1 = objectMapper.readValue(jsonStr, new TypeReference<HashMap<String, Student>>() {
});
System.out.println("studentHashMap1:\n" + studentHashMap1);
//List
List<Student> studentList = new ArrayList<>();
studentList.add(stu1);
studentList.add(stu2);
JacksonUtil.encode2File(studentList, CurPath + "/jacksontest2.json");
String jsonStr2 = JacksonUtil.encode(studentList);
List<Student> studentList1 = objectMapper.readValue(jsonStr2, objectMapper.getTypeFactory().constructParametricType(ArrayList.class, Student.class));
System.out.println("studentList1:\n" + studentList1);
}
4)、Fastjson解析
导入Fastjson依赖
implementation 'com.alibaba:1 fastjson:1.2.57'
使用Fastjson解析
public static void main(String[] args) {
//TODO:
Student student = new Student();
student.setName("FastJson");
student.setSax("男");
student.setAge(28);
student.addCourse(new Course("英语", 78.3f));
student.addCourse(new Course("语文", 88.9f));
student.addCourse(new Course("数学", 48.2f));
//1. 生成json文件
File file = new File(CurPath + "/fastjsontest.json");
FileOutputStream fileOutputStream = new FileOutputStream(file);
JSONObject.writeJSONString(fileOutputStream, student);
//2. 反序列化
Student student1 = JSONObject.parseObject(new FileInputStream(file), Student.class);
System.out.println(student1);
}
四、自定义一个JSON解析库
编写一个JSON解析器实际上就是一个方法,它的输入是一个表示JSON的字符串,输出是结构化的对应到语言本身的数据结构。
一般来说,解析过程包括词法分析和语法分析两个阶段。
- 1、
词法分析阶段
的目标是按照构词规则将 JSON字符串解析成 Token 流。
比如有如下的 JSON 字符串:
{
"key" : "value",
}
结果词法分析后,得到一组 Token,如下:
词法分析JSON 所规定的数据类型:
- BEGIN_OBJECT => ( { )
- END_OBJECT => ( } )
- BEGNT_ARRAY => ( [ )
- END_ARRAY => ( ] )
- NULL => ( null )
- NUMBER => ( 数字 )
- STRING => ( 字符串 )
- BOOLEAN => ( true/false )
- SEP_COLON =>( : )
- SEP_COMMA => ( , )
- 2、
语法分析
的目的是根据 JSON 文法检查上面Token 序列所构成的 JSON 结构是否合法。
所规定的类型:
n => null
t => true
f => false
" => string
0-9/- => number
[ => array
{ => object
网友评论