Java解析XML与JSON简谈

Java解析XML与JSON简谈

1 XML

1.1 介绍

  • XML 指可扩展标记语言(EXtensible Markup Language)
  • XML 具有平台无关性,是一门独立的标记语言
  • XML具有自我描述性
  • XML 是不作为的
  • XML 被设计为传输和存储数据,其焦点是数据的内容

1.2 作用

  • 网络数据传输
  • 数据存储
  • 配置文件
  • XML是一种语言,不要将其言狭隘的理解成XML文件

1.3 语法格式

XML文档是一种树结构,从“树根”开始,分出许多“枝叶”,这些“枝叶”又可以分出下一级“枝叶”,这样循环分枝,就形成了一颗XML树。XML具有自我描述性,这个自我描述性主要体现在XML的标签上(也称为元素 / 标记 / 节点)。例如,下面一段XML文档:

<?xml version="1.0" encoding="UTF-8"?>
<school name="mySchool" id="1">
    <class name="class1" id="1">
        <student id="1">
            <name>学生1</name>
            <gender>男</gender>
            <info>你好,我是学生1</info>
        </student>
        <student id="2">
            <name>学生2</name>
            <gender>女</gender>
            <info>你好,我是学生2</info>
        </student>
        <student id="3">
            <name>学生3</name>
            <gender>男</gender>
            <info>你好,我是学生3</info>
        </student>
    </class>
</school>

第一行是XML的声明。它描述了XML文档的版本是1.0,使用的编码格式是UTF-8

第二行的<school>标签是根标签,它描述了这个XML文档存储的数据是关于学校的数据,标签中的name属性描述了学校的名称,id属性描述了学校的id。

<school>标签下有子标签<class>,同样<class>标签也具有自我描述性。

<class>也有子标签<student>,不过它一共有三个子标签<student>,这三个<student>标签互为兄弟标签,有不同的id属性值,可以用于在遍历XML文档树时唯一确定一个<student>标签。

由此,在阅读XML文档时,我们可以很清楚地知道文档在描述什么,在遍历XML树时也能很方便地找到一个标签的所在位置并且获取它存储的数据。

不过在编写XML文档时,我们要遵守XML的语法格式,才能让我们编写出来的XML文档不出错误并且具有良好的自我描述性。

  • 每一个XML文档都必须要有一个文档声明<?xml version="xml文档版本" encoding="编码格式"?>,必须有且仅有一个根标签。
  • 标签名称可以包含字母,数字及其它字符,但不能以数字开头,也不能包含可能导致文档解析错误的标点符号。
  • 所有的标签都必须有结束标签<school></school>是正确的标签格式,<school>是错误的标签格式,会导致XML文档解析错误。
  • 标签名称对大小写敏感,开始标签与结束标签的名称必须是相同的字符串例如:<School></school>不是一对匹配的标签,<school></school>是一对匹配的标签,能被正确解析。
  • 标签必须正确地嵌套,不能一个标签与另一个标签交叉嵌套,<name>学生3</name><gender>男</gender>是正确的嵌套格式,而<name>学生3<gender></name>男</gender>是错误的嵌套格式。
  • 标签中可以有属性,属性必须写在开始标签中,属性由属性名与属性值组成,必须遵守格式属性名="属性值"编写,同一个开始标签中的属性名不允许重复,多个属性之间用空格隔开。
  • XML文档中的注释格式为<!--注释的内容-->,注释不允许嵌套也不允许写在文档声明之前。
  • 除了必须要遵循的XML语法规范(如定义标签,属性等)外,在文档中其它位置最好不要使用< > & ' "等可能导致文档解析错误的字符(”<“会被解析成新标签的开始,“&”会被解析成实体引用的开始,其余字符虽然不会导致解析错误,但以防万一还是不要使用),而是使用实体引用&lt; &gt; &amp; &apos; &quot; 等来代替它们。
  • 开始标签与结束标签之间既可以包含其他标签,也可以包含文本内容,甚至是两者的混合物。

1.4 XML CDATA

所有XML文档中的文本均会被解析器解析,只有CDATA区段中的文本会被解析器忽略,CDATA存在的意义就是存储不应该由XML解析器解析的文本数据。例如在一段JavaScript代码中包含大量的“<”和"&"字符,如果要将这些字符都转换为实体引用,显然比较不现实,所以要将这段JavaScript代码放在CDATA区段中。CDATA的定义格式为<![CDATA[存储数据的位置]]>。显然CDATA存储数据的位置中不允许提前出现]]>,在CDATA中再嵌套CDATA也是不允许且无意义的。

1.5 Java解析及生成XML文档

1.5.1 Java解析XML的四种方式
1. SAX解析
   解析方式是事件驱动机制!
   SAX解析器, 逐行读取XML文件解析, 每当解析到一个标签的开始/结束/内容/属性时, 触发事件.
   我们可以编写程序在这些事件发生时, 进行相应的处理.

   优点:

          1. 分析能够立即开始, 而不是等待所有的数据被处理.
             2. 逐行加载, 节省内存, 有助于解析大于系统内存的文档.
                3. 有时不必解析整个文档,它可以在某个条件得到满足时停止解析.
                   缺点:
                4. 单向解析,无法定位文档层次,无法同时访问同一文档的不同部分数据(因为逐行解析, 当解析第n行是, 第n-1行已经被释放了, 无法在进行操作了).
                5. 无法得知事件发生时元素的层次, 只能自己维护节点的父/子关系.
                6. 只读解析方式, 无法修改XML文档的内容.

2. DOM解析
   是用与平台和语言无关的方式表示XML文档的官方W3C标准, 分析该结构通常需要加载整个文档和内存中建立文档树模	型. 程序员可以通过操作文档树, 来完成数据的获取 修改 删除等.

   优点:

          1. 文档在内存中加载, 允许对数据和结构做出更改.
             2. 访问是双向的, 可以在任何时候在树中双向解析数据.
                缺点:
                  1. 文档全部加载在内存中, 消耗资源大.

3. JDOM解析
   目的是成为Java特定文档模型, 它简化与XML的交互并且比使用DOM实现更快. 由于是第一个Java特定模型,JDOM一直得到大力推广和促进.
   JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%).

   优点:

          1. 使用具体类而不是接口,简化了DOM的API.
             2. 大量使用了Java集合类,方便了Java开发人员.
                缺点:
                  1. 没有较好的灵活性.
                  2. 性能不是那么优异.

4. DOM4J解析
   它是JDOM的一种智能分支. 它合并了许多超出基本XML文档表示的功能, 包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理. 它还提供了构建文档表示的选项,DOM4J是一个非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点, 同时它也是一个开放源代码的软件. 如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML.
   目前许多开源项目中大量采用DOM4J, 例如:Hibernate.
1.5.2 DOM4J解析XML

DOM4j包中一些常用的方法如下:

1.	创建一个XML读取工具对象
	SAXReader sr = new SAXReader();
2.	根据XML文档的输入流,得到文档对象
    Document doc = sr.read(FileInputStream fis);
3.	通过文档对象,获取XML文档中的根元素
    Element root = doc.getRootElement();
4.	获取节点名称
    String getName();
5.	获取节点内容
    String getText();
6.	根据子节点的名称 , 获取匹配名称的第一个子节点对象
    Element element(String name);
7.	获取所有的子节点对象
	List<Element> elements();
8.	获取节点的属性值
    String attributeValue(String name);
9.	获取子节点的内容
    String elementText(String name);
10.	获取节点的所有属性值
    List<Attribute> attributes();

在简单了解XML语言之后,我们来用DOM4J解析一份简单的XML文档,XML文档如下:

<?xml version="1.0" encoding="UTF-8"?>
<school name="mySchool" id="1">
    <class name="class1" id="1">
        <teachers>
            <teacher id="1" type="math">
                <name>数学老师</name>
                <gender>男</gender>
                <info>我是class1的数学老师</info>
            </teacher>
            <teacher id="2" type="chinese">
                <name>语文老师</name>
                <gender>女</gender>
                <info>我是class1的语文老师</info>
            </teacher>
        </teachers>
        <students>
            <student id="1">
                <name>学生1</name>
                <gender>男</gender>
                <info>你好,我是学生1</info>
            </student>
            <student id="2">
                <name>学生2</name>
                <gender>女</gender>
                <info>你好,我是学生2</info>
            </student>
            <student id="3">
                <name>学生3</name>
                <gender>男</gender>
                <info>你好,我是学生3</info>
            </student>
        </students>
    </class>
</school>

显然我们可以通过获取文件输入流然后打印的方法将这个文件的所有内容打印在控制台上。但是现在我们并不想获取整颗XML树,我们想给定一个元素,获取以它为根元素的XML树,我们用DOM4J包中的一些方法来实现要求。

代码如下:

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.util.List;

public class Demo1 {
    public static void main(String[] args) throws IOException, DocumentException {
        //1.    获取系统路径分隔符。
        //String fileSeparator = System.getProperty("file.separator");
        //2.    获取系统文件分隔符。
        //String pathSeparator = System.getProperty("path.separator");
        //3.    创建文件路径字符串(可以将”/“符号改成pathSeparator)。
        String XMLPath = "src/com/java/school.xml";
        //4.    获取XML文件所在路径。
        FileInputStream fis = new FileInputStream(XMLPath);
        //5.    创建XML读取工具对象。
        SAXReader sr = new SAXReader();
        //6.    读取XML文档输入流,并得到文档对象。
        Document doc = sr.read(fis);
        //7.    通过文档对象,获取XML文档中的根元素对象。
        Element root = doc.getRootElement();
        //8.    获取根元素下的子元素students。
        Element schoolClass = root.element("class");
        //9.    根据根元素打印整颗XML树。
        System.out.println(XMLTree(schoolClass));
        //10.    关闭输入流。
        fis.close();
    }

    /**
     * 打印以当前元素作为根的XML树。
     * @param e XML树中任意元素。
     * @return 以当前元素作为根的XML树
     */
    private static String XMLTree(Element e) {
        return XMLTree(e, 0);
    }

    /**
     * 打印以当前元素作为根的XML树。
     * @param e XML树中任意元素。
     * @param level 与给定元素比较的层级。
     * @return 以当前元素作为根的XML树
     */
    private static String XMLTree(Element e, Integer level) {
        List<Element> elements;
        StringBuilder sb = new StringBuilder();
        //1.    向StringBuilder中添加此元素的开始标签。
        for (int i = 0; i < level; i++)
            sb.append("\t");
        sb.append("<" + e.getName());
        List<Attribute> attributes = e.attributes();
        for (Attribute a : attributes)
            sb.append(" " + a.getName() + "=\"" + a.getValue() + "\"");
        //2.    判断此元素是否还有子元素,
        //          如果有就换一行并向StringBuilder中添加此元素的所有子元素。
        //          如果没有就向StringBuilder中添加此元素的文本内容。
        if ((elements = e.elements()).isEmpty()) {
            sb.append(">" + e.getText() + "</" + e.getName() + ">\n");
        } else {
            sb.append(">\n");
            for(Element element : elements) {
                sb.append(XMLTree(element, level + 1));
            }
            for (int i = 0; i < level; i++)
                sb.append("\t");
            sb.append("</" + e.getName() + ">\n");
        }
        return sb.toString();
    }
}

输出的结果:

<class name="class1" id="1">
	<teachers>
		<teacher id="1" type="math">
			<name>数学老师</name>
			<gender>男</gender>
			<info>我是class1的数学老师</info>
		</teacher>
		<teacher id="2" type="chinese">
			<name>语文老师</name>
			<gender>女</gender>
			<info>我是class1的语文老师</info>
		</teacher>
	</teachers>
	<students>
		<student id="1">
			<name>学生1</name>
			<gender>男</gender>
			<info>你好,我是学生1</info>
		</student>
		<student id="2">
			<name>学生2</name>
			<gender>女</gender>
			<info>你好,我是学生2</info>
		</student>
		<student id="3">
			<name>学生3</name>
			<gender>男</gender>
			<info>你好,我是学生3</info>
		</student>
	</students>
</class>
1.5.3 DOM4J设置XML

除了对XML文档进行获取内容外,我们还可以通过DOM4J中的一些方法设置XML文档的元素,我们试着来设置一下XML文档的元素。

import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.FileInputStream;
import java.io.IOException;

public class Demo2 {
    public static void main(String[] args) throws IOException, DocumentException {
        //1.    获取系统路径分隔符。
        //String fileSeparator = System.getProperty("file.separator");
        //2.    获取系统文件分隔符。
        //String pathSeparator = System.getProperty("path.separator");
        //3.    创建文件路径字符串(可以将”/“符号改成pathSeparator)。
        String XMLPath = "src/com/java/school.xml";
        //4.    获取XML文件所在路径。
        FileInputStream fis = new FileInputStream(XMLPath);
        //5.    创建XML读取工具对象。
        SAXReader sr = new SAXReader();
        //6.    读取XML文档输入流,并得到文档对象。
        Document doc = sr.read(fis);
        //7.    通过文档对象,获取XML文档中的根元素对象。
        Element root = doc.getRootElement();
        //8.    为选择的节点添加子元素。
        Element class2 = root.addElement("class");
        //9.    为子元素添加两个属性。
        class2.addAttribute("name", "class2");
        class2.addAttribute("id", "2");
        //10.   为子元素添加文本内容。
        class2.addText("这是class2的文本内容");
        //11.   通过XPath获取指定的子节点,并强制类型转换成Element。
        Element element = (Element) root.selectSingleNode("//class[@name='class2'][@id='2']");
        //12.   调用Demo1的XMLTree方法获取以子元素为根元素的XML树并打印。
        System.out.println(Demo1.XMLTree(element));
        //13.   关闭输入流。
        fis.close();
    }
}

输出的结果:

<class name="class2" id="2">这是class2的文本内容</class>

可以看到我们成功地为XML文档的根节点又添加了一个名称为class2,id为2的子节点,并添加了它的文本内容,如果后续我们还要添加节点,也是按照上面的流程来。

在上面代码的第11步中,我们使用了XPath来获取到指定的节点,因为我们给每一个class节点都设置了name与id,所以可以根据这两个属性唯一确定一个class节点。下面一起来看看如何使用XPath来获取XML文档中的指定节点。

1.5.4 DOM4J-XPATH解析XML
通过Node类的方法, 来完成查找

(Node是 Document 与 Element 的父接口)

路径表达式:
    1  / 	: 从根节点开始查找
    2  // 	: 从发起查找的节点位置 查找后代节点 
    3  . 	: 查找当前节点
    4  .. 	: 查找父节点
    5  @ 	: 选择属性:
                    属性使用方式:
                    [@属性名='值']
                    [@属性名>'值']
                    [@属性名<'值']
                    [@属性名!='值']

XPath的使用案例如下:

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.FileInputStream;
import java.io.IOException;

public class Demo3 {
    public static void main(String[] args) throws IOException, DocumentException {
        //1.    获取系统路径分隔符。
        //String fileSeparator = System.getProperty("file.separator");
        //2.    获取系统文件分隔符。
        //String pathSeparator = System.getProperty("path.separator");
        //3.    创建文件路径字符串(可以将”/“符号改成pathSeparator)。
        String XMLPath = "src/com/java/school.xml";
        //4.    获取XML文件所在路径。
        FileInputStream fis = new FileInputStream(XMLPath);
        //5.    创建XML读取工具对象。
        SAXReader sr = new SAXReader();
        //6.    读取XML文档输入流,并得到文档对象。
        Document doc = sr.read(fis);
        //7.    从根节点开始查找name="class1",id="1"的子节点并打印出XML树。
        Element element1 = (Element) doc.selectSingleNode("/school//class[@name='class1'][@id='1']");
        System.out.println("从根节点开始查找name=\"class1\",id=\"1\"的子节点并打印出XML树。");
        System.out.println(Demo1.XMLTree(element1));
        //8.    查找上面节点的teachers子节点并打印出XML树。
        Element element2 = (Element) element1.selectSingleNode("//teachers");
        System.out.println("查找上面节点的teachers子节点并打印出XML树。");
        System.out.println(Demo1.XMLTree(element2));
        //9.    查找teachers节点的父节点的父节点(根节点)并打印出XML树。
        Element element3 = (Element) element2.selectSingleNode("..//..");
        System.out.println("查找teachers节点的父节点的父节点(根节点)并打印出XML树。");
        System.out.println(Demo1.XMLTree(element3));
        //10.   关闭输入流。
        fis.close();
    }
}

输出的结果:

从根节点开始查找name="class1",id="1"的子节点并打印出XML树。
<class name="class1" id="1">
	<teachers>
		<teacher id="1" type="math">
			<name>数学老师</name>
			<gender>男</gender>
			<info>我是class1的数学老师</info>
		</teacher>
		<teacher id="2" type="chinese">
			<name>语文老师</name>
			<gender>女</gender>
			<info>我是class1的语文老师</info>
		</teacher>
	</teachers>
	<students>
		<student id="1">
			<name>学生1</name>
			<gender>男</gender>
			<info>你好,我是学生1</info>
		</student>
		<student id="2">
			<name>学生2</name>
			<gender>女</gender>
			<info>你好,我是学生2</info>
		</student>
		<student id="3">
			<name>学生3</name>
			<gender>男</gender>
			<info>你好,我是学生3</info>
		</student>
	</students>
</class>

查找上面节点的teachers子节点并打印出XML树。
<teachers>
	<teacher id="1" type="math">
		<name>数学老师</name>
		<gender>男</gender>
		<info>我是class1的数学老师</info>
	</teacher>
	<teacher id="2" type="chinese">
		<name>语文老师</name>
		<gender>女</gender>
		<info>我是class1的语文老师</info>
	</teacher>
</teachers>

查找teachers节点的父节点的父节点(根节点)并打印出XML树。
<school name="mySchool" id="1">
	<class name="class1" id="1">
		<teachers>
			<teacher id="1" type="math">
				<name>数学老师</name>
				<gender>男</gender>
				<info>我是class1的数学老师</info>
			</teacher>
			<teacher id="2" type="chinese">
				<name>语文老师</name>
				<gender>女</gender>
				<info>我是class1的语文老师</info>
			</teacher>
		</teachers>
		<students>
			<student id="1">
				<name>学生1</name>
				<gender>男</gender>
				<info>你好,我是学生1</info>
			</student>
			<student id="2">
				<name>学生2</name>
				<gender>女</gender>
				<info>你好,我是学生2</info>
			</student>
			<student id="3">
				<name>学生3</name>
				<gender>男</gender>
				<info>你好,我是学生3</info>
			</student>
		</students>
	</class>
</school>

以上介绍的都是通过DOM4J读取解析XML文件的一些方法,其实DOM4J还可以用来生成XML,我们来一起看看如何使用DOM4J中的方法生成XML。

1.5.5 DOM4J生成XML

我们来生成一个简单的XML文档并将其存储在磁盘中,最后打印该文档的XML树看看是否正确:

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo4 {
    public static void main(String[] args) throws IOException, DocumentException {
        //1.    通过文档帮助器(DocumentHelper),创建空的文档对象。
        Document doc = DocumentHelper.createDocument();
        //2.    通过文档对象,向其中添加根节点school。
        Element root = doc.addElement("school");
        //2.1   为根节点school添加属性。
        root.addAttribute("name", "mySchool");
        root.addAttribute("id", "1");
        //3.    通过根节点对象root,添加子节点class。
        Element class1 = root.addElement("class");
        //3.1   为子节点class添加属性。
        class1.addAttribute("name", "class1");
        class1.addAttribute("id", "1");
        //3.2   为子节点class添加文本内容。
        class1.addText("这是class1的文本内容");
        //4.    创建一个文件输出流,用于存储XML文件。
        FileOutputStream fos = new FileOutputStream("src/com/java/school2.xml");
        //5.    将文件输出流,转换为XML文档输出流。
        XMLWriter xw = new XMLWriter(fos);
        //6.    写出文档。
        xw.write(doc);
        //7.    释放资源。
        xw.close();
        //8.    获取刚才创建的XML文档并将其XML树打印看看是否正确生成。
        String XMLPath = "src/com/java/school2.xml";
        FileInputStream fis = new FileInputStream(XMLPath);
        SAXReader sr = new SAXReader();
        Document document = sr.read(fis);
        System.out.println(Demo1.XMLTree(document.getRootElement()));
    }
}

输出的结果:

<school name="mySchool" id="1">
	<class name="class1" id="1">这是class1的文本内容</class>
</school>

可以看到我们成功生产了一个简单的XML文档并存储到了磁盘中,也能正确解析出来。

1.5.6 使用XStream实现对象与XML之间的转换

在编程中,我们可能不会凭空构造一个XML,而是会根据类对象构造对应地的XML文件存储数据,并且也要根据获得的XML文件将其转换成类对象,这就可以用XStream中的方法来实现,XStream可以很快速便捷地将Java中的对象和XML字符串实现相互转换。下面我们构造一个bean类Student来看看具体怎么写。

public class Student {
    private String name;
    private int age;
    private String gender;
    private String info;

    public Student() {
    }

    public Student(String name, int age, String gender, String info) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", info='" + info + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class Demo5 {
    public static void main(String[] args) {
        //1.    创建XStream对象。
        XStream x = new XStream(new DomDriver());
        //2.    创建Student对象。
        Student s1 = new Student("学生1", 18, "男", "大家好,我是学生1");
        //3.   修改类生成的节点名称(默认节点名称为 包名.类名)。
        x.alias("student", Student.class);
        //4.   传入student对象,生成XML字符串。
        String str = x.toXML(s1);
        System.out.println("打印出s1对象生成的XML字符串。");
        System.out.println(str);
        //5.    根据XML字符串生成相应对象。
        Student s = (Student) x.fromXML(str);
        System.out.println("打印出根据XML字符串生成的student对象。");
        System.out.println(s);
    }
}

输出的结果:

打印出s1对象生成的XML字符串。
<student>
  <name>学生1</name>
  <age>18</age>
  <gender>男</gender>
  <info>大家好,我是学生1</info>
</student>
打印出根据XML字符串生成的student对象。
Student{name='学生1', age=18, gender='男', info='大家好,我是学生1'}

可以看到用XStream很方便就实现了XML与对象之间的互转。

以上就是XML的简单介绍,以及在Java中该如何解析与转换XML的简单案例。

下面我们来看另一种数据传输的格式JSON,相比较XML,它更易于阅读与数据传输和解析。

2 JSON

2.1 介绍

  • JSON指JavaScript Object Notation( JS对象简谱), 是一种轻量级的数据交换格式。
  • JSON 是存储和交换文本信息的语法,类似 XML。
  • JSON 比 XML 更小、更快,更易解析。
  • JSON不使用保留字

2.2 作用

  • 主要用来进行数据的传输。

2.3 语法格式

  • JSON用键值对来存储数据,键是数据的名称,值是数据的内容,数据之间由逗号分隔,大括号中存储对象,中括号中存储数组,两者之间都可以随意嵌套,但是不能交叉,像XML一样,一定要保证符号的开始与结尾之中不会多出其它对象或数组的部分。
  • JSON的值可以是数字,字符串,逻辑值,对象,数组,或者是null值。键和字符串值必须用英文双引号引住。

在了解了JSON的基本语法格式之后,我们来试着把上面的描述school的XML文件转换成JSON格式看看(由于class是Java中的保留字,所以这里改成了myClass):

{
    "name": "mySchool",
    "id": 1,
    "myClass": {
        "name": "class1",
        "id": 1,
        "teachers": [
            {
                "id": 1,
                "type": "math",
                "name": "数学老师",
                "gender": "男",
                "info": "我是class1的数学老师"
            },
            {
                "id": 2,
                "type": "chinese",
                "name": "语文老师",
                "gender": "女",
                "info": "我是class1的语文老师"
            }
        ],
        "students": [
            {
                "id": 1,
                "name": "学生1",
                "gender": "男",
                "info": "你好,我是学生1"
            },
            {
                "id": 2,
                "name": "学生2",
                "gender": "女",
                "info": "你好,我是学生2"
            },
            {
                "id": 3,
                "name": "学生3",
                "gender": "男",
                "info": "你好,我是学生3"
            }
        ]
    }
}

我们发现转换成JSON之后,数据的大小变小了,但是可读性不仅没有降低,还提升了。既然JSON是用于数据传输的交换格式,那数据的解析肯定是它关注的重点,在Java中,我们比较常用的Java解析的包有两个:Gson与FastJson。我们用Gson来解析上面的JSON数据。

2.5 使用Gson解析JSON数据

为了将以上JSON数据转换为对象,我们需要构造以下几个bean类:School,MyClass,Teacher,Student,具体如下。

public class School {
    private String name;
    private Integer id;
    private MyClass myClass;

    public School() {
    }

    public School(String name, Integer id, MyClass myClass) {
        this.name = name;
        this.id = id;
        this.myClass = myClass;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", myClass=" + myClass +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public MyClass getMyClass() {
        return myClass;
    }

    public void setMyClass(MyClass myclass) {
        this.myClass = myclass;
    }
}
import java.util.List;

public class MyClass {
    private String name;
    private String id;
    private List<Teacher> teachers;
    private List<Student> students;

    public MyClass() {
    }

    public MyClass(String name, String id, List<Teacher> teachers, List<Student> students) {
        this.name = name;
        this.id = id;
        this.teachers = teachers;
        this.students = students;
    }

    @Override
    public String toString() {
        return "myClass{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                ", teachers=" + teachers +
                ", students=" + students +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public List<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }
}
public class Teacher {
    private Integer id;
    private String type;
    private String name;
    private String gender;
    private String info;

    public Teacher() {
    }

    public Teacher(Integer id, String type, String name, String gender, String info) {
        this.id = id;
        this.type = type;
        this.name = name;
        this.gender = gender;
        this.info = info;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", info='" + info + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}
public class Student {
    private String name;
    private Integer age;
    private String gender;
    private String info;

    public Student() {
    }

    public Student(String name, Integer age, String gender, String info) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", info='" + info + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

然后我们写将JSON转换成对象,对象转换成JSON的案例。

import com.google.gson.Gson;

import java.io.*;

public class Demo6 {
    public static void main(String[] args) throws IOException {
        //1.    获取JSON文件的输入流。
        FileInputStream fis = new FileInputStream("src/com/java/school.json");
        //2.    转换成字节流方便读取。
        BufferedReader br = new BufferedReader(new InputStreamReader(fis));
        //3.    新建StringBuilder对象存储JSON字符串。
        StringBuilder sb = new StringBuilder();
        String str;
        while ((str = br.readLine()) != null)
            sb.append(str);
        //4.    将字符串转换为School对象并打印。
        School school = new Gson().fromJson(sb.toString(), School.class);
        System.out.println("JSON转换成School对象并打印。");
        System.out.println(school);
        //5.    对象转换成JSON字符串并打印。
        System.out.println("School对象转换成JSON字符串并打印。");
        System.out.println(new Gson().toJson(school));
        //6.    关闭流。
        br.close();
    }
}

输出结果:

JSON转换成School对象并打印。
School{name='mySchool', id=1, myClass=myClass{name='class1', id='1', teachers=[Teacher{id=1, type='math', name='数学老师', gender='男', info='我是class1的数学老师'}, Teacher{id=2, type='chinese', name='语文老师', gender='女', info='我是class1的语文老师'}], students=[Student{name='学生1', age=null, gender='男', info='你好,我是学生1'}, Student{name='学生2', age=null, gender='女', info='你好,我是学生2'}, Student{name='学生3', age=null, gender='男', info='你好,我是学生3'}]}}
School对象转换成JSON字符串并打印。
{"name":"mySchool","id":1,"myClass":{"name":"class1","id":"1","teachers":[{"id":1,"type":"math","name":"数学老师","gender":"男","info":"我是class1的数学老师"},{"id":2,"type":"chinese","name":"语文老师","gender":"女","info":"我是class1的语文老师"}],"students":[{"name":"学生1","gender":"男","info":"你好,我是学生1"},{"name":"学生2","gender":"女","info":"你好,我是学生2"},{"name":"学生3","gender":"男","info":"你好,我是学生3"}]}}

好了,现在我们成功地实现了JSON与对象地相互转换。

2.6 使用FastJson解析JSON数据

用法与Gson类似,这里就不展开写了。

  • 将对象转换为JSON字符串
School school = JSON.parseObject(sb.toString(), School.class);
  • 将JSON字符串转换为对象
String str = JSON.toJSONString(school);

打印到控制台上的结果与Gson一样。

posted @ 2021-08-13 16:30  leafruomu  阅读(702)  评论(0)    收藏  举报