MOOC 个人学习笔记

# 1.XML 简介

  • 可扩展标记语言:意义 + 数据
  • 标签可自行定义,具有自我描述性
  • 纯文本表示,跨系统 / 平台 / 语言
  • W3C 标准 (1998 年,W3C 发布了 XML1.0,包括几乎所有的 Unicode 字符)
<Student>
    <name>Tom</name>
    <age>20</age>    
</Student>

# XML 结构

  • 常规语法
    • 任何的起始标签都必须有一个结束标签
    • 简化写法,例如,<name></name > 可以写为 < name/>
    • 大小写敏感,如 <name> 和 < Name > 不一样
    • 每个文件都要有一个根元素
    • 标签必须按合适的顺序进行嵌套,不可错位
    • 所有的特性都必须有值,且在值的周围加上引号
    • 需要转义字符,如 “<” 需要用 < 代替
    • 注释:<!-- 注释内容 -->

# XML 扩展

  • DTD(Document Type Definition)
    • 定义 XML 文档的结构
    • 使用一系列合法的元素来定义文档结构
    • 可嵌套在 xml 文档中,或者在 xml 中引用
  • XML Schema(XSD,XML Schema Definition)
    • 定义 XML 文档的结构,DTD 的继任者
    • 支持数据类型,可扩展,功能更完善、强大
    • 采用 xml 编写
  • XSL
    • 扩展样式表语言 (eXtensible Stylesheet Language)
    • XSL 作用于 XML,等同于 CSS 作用于 HTML
    • 内容
      • XSLT: 转换 XML 文档
      • XPath: 在 XML 文档中导航
      • XSL-FO: 格式化 XML 文档

# XML 解析

  • 树结构
    • DOM: Document Object Model 文档对象模型,擅长 (小规模) 读 / 写
  • 流结构
    • SAX: Simple API for XML 流机制解释器 (推模式),擅长读
    • Stax: The Streaming API for XML 流机制解释器 (拉模式),擅长读,JDK 6 引入

# 2.XML 解析(基于 DOM)

  • DOM 是 W3C 处理 XML 的标准 API
    • 直观易用
    • 其处理方式是将 XML 整个作为类似树结构的方式读入内存中以便操作及解析,方便修改
    • 解析大数据量的 XML 文件,会遇到内存泄露及程序崩溃的风险
<font>
    <name>Helvetica</name>
    <size>36</size>
</font>

# DOM 类

  • DocumentBuilder 解析类,parse 方法
  • Node 节点主接口,getChildNodes 返回一个 NodeList
  • NodeList 节点列表,每个元素是一个 Node
  • Document 文档根节点
  • Element 标签节点元素 (每一个标签都是标签节点)
  • Text 节点 (包含在 XML 元素内的,都算 Text 节点)
  • Attr 节点 (每个属性节点)

# 读取:

# 1. 自上而下遍历(DFS):
//DOM 解析 xml 文件
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("pom.xml");
// 获取所有一级子节点
NodeList usersList = document.getChildNodes();
System.out.println(usersList.getLength());
for (int i = 0; i < usersList.getLength(); i++) {
    Node users = usersList.item(i);
    // 获取二级子节点列表
    NodeList userList = users.getChildNodes();
    System.out.println("==" + userList.getLength());
    for (int j = 0; j < userList.getLength(); j++) {
        Node user = userList.item(j);
        // 排除 whitespace
        if (user.getNodeType() == Node.ELEMENT_NODE) {
            // 获取三级子节点列表
            NodeList metaList = user.getChildNodes();
            System.out.println("====" + metaList.getLength());
            //......
        }
    }
}
# 2. 根据名称进行搜索
//DOM 解析 xml 文件
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("pom.xml");
Element rootElement =document.getDocumentElement();
// 获取所有标签名为 name 的节点
NodeList nodeList=rootElement.getElementsByTagName("name");
if(nodeList!=null) {
    for(int i=0;i<nodeList.getLength();i++) {
        Element element = (Element)nodeList.item(i);
        System.out.println(element.getNodeName()+"="+element.getTextContent());
    }
}

# 写入:

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
// 新建 Documnet 根节点
Document document = dbBuilder.newDocument();
if (document != null) {
    Element docx = document.createElement("document");    // 均采用 Doucument 创建元素
    Element element = document.createElement("element");
    element.setAttribute("type", "paragraph");    
    element.setAttribute("alignment", "left"); //element 新增两个属性
    Element object = document.createElement("object");
    object.setAttribute("type", "text");
    Element text = document.createElement("text");
    text.appendChild(document.createTextNode("abcdefg")); //text 节点赋值
    Element bold = document.createElement("bold");
    bold.appendChild(document.createTextNode("true")); //bold 节点赋值
    object.appendChild(text);
    object.appendChild(bold);
    element.appendChild(object);
    docx.appendChild(element);
    document.appendChild(docx);    // 创建树状结构
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    DOMSource source = new DOMSource(document);
    // 定义目标文件
    File file = new File("dom_result.xml");
    StreamResult result = new StreamResult(file);
    // 将 xml 内容写入到文件中
    transformer.transform(source, result);
}

# 3.XML 解析(基于 SAX)

Simple API for XML

  • 采用事件 / 流模型来解析 XML 文档,更快速、更轻量
  • 有选择的解析和访问,不像 DOM 加载整个文档,内存要求较低
  • SAX 对 XML 文档的解析为一次性读取,不创建 / 不存储文档对象,很难同时访问文档中的多处数据
  • 推模型。当它每发现一个节点就引发一个事件,而我们需要编写这些事件的处理程序
  • 关键类:DefaultHandler
public class SAXReader {
    public static void main(String[] args) throws SAXException, IOException {
        XMLReader parser = XMLReaderFactory.createXMLReader();
        BookHandler bookHandler = new BookHandler();
        parser.setContentHandler(bookHandler);
        parser.parse("books.xml");
        System.out.println(bookHandler.getNameList());
    }
}
class BookHandler extends DefaultHandler {
    private List<String> nameList;
    private boolean title = false;
    public List<String> getNameList() {
        return nameList;
    }
    //xml 文档加载时
    public void startDocument() throws SAXException {
        System.out.println("Start parsing document...");
        nameList = new ArrayList<String>();
    }
    // 文档解析结束
    public void endDocument() throws SAXException {
        System.out.println("End");
    }
    // 访问某一个元素
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        if (qName.equals("title")) {
            title = true;
        }
    }
    // 结束访问元素
    public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
        // End of processing current element
        if (title) {
            title = false;
        }
    }
    // 访问元素正文
    public void characters(char[] ch, int start, int length) {
        if (title) {
            String bookTitle = new String(ch, start, length);
            System.out.println("Book title: " + bookTitle);
            nameList.add(bookTitle);
        }
    }
}

# 4.XML 解析(基于 Stax)

Streaming API for XML

  • 流模型中的拉模型
  • 在遍历文档时,会把感兴趣的部分从读取器中拉出,不需要引发事件,允许我们选择性地处理节点。这大大提高了灵活性,以及整体效率
  • 两套处理 API
    • 基于指针的 API, XMLStreamReader
    • 基于迭代器的 API,XMLEventReader
public class StaxReader {
    public static void main(String[] args) {
        StaxReader.readByStream();    // 基于指针遍历
        StaxReader.readByEvent();    // 基于迭代器遍历
    }
    // 流模式
    public static void readByStream() {
        // 固定写法
        String xmlFile = "books.xml";
        XMLInputFactory factory = XMLInputFactory.newFactory();
        XMLStreamReader streamReader = null;
        try {
            streamReader = factory.createXMLStreamReader(new FileReader(xmlFile));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
        // 基于指针遍历
        try {
            while (streamReader.hasNext()) {
                int event = streamReader.next();
                // 如果是元素的开始
                if (event == XMLStreamConstants.START_ELEMENT) {
                    // 列出所有书籍名称
                    if ("title".equalsIgnoreCase(streamReader.getLocalName())) {
                        System.out.println("title:" + streamReader.getElementText());
                    }
                }
            }
            streamReader.close();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
    }
    // 事件模式
    public static void readByEvent() {
        String xmlFile = "books.xml";
        XMLInputFactory factory = XMLInputFactory.newInstance();
        boolean titleFlag = false;
        try {
            // 创建基于迭代器的事件读取器对象
            XMLEventReader eventReader = factory.createXMLEventReader(new FileReader(xmlFile));
            // 遍历 Event 迭代器
            while (eventReader.hasNext()) {
                XMLEvent event = eventReader.nextEvent();
                // 如果事件对象是元素的开始
                if (event.isStartElement()) {
                    // 转换成开始元素事件对象
                    StartElement start = event.asStartElement();
                    // 打印元素标签的本地名称
                    String name = start.getName().getLocalPart();
                    // System.out.print(start.getName().getLocalPart());
                    if (name.equals("title")) {
                        titleFlag = true;
                        System.out.print("title:");
                    }
                    // 取得所有属性
                    Iterator attrs = start.getAttributes();
                    while (attrs.hasNext()) {
                        // 打印所有属性信息
                        Attribute attr = (Attribute) attrs.next();
                        // System.out.print(":" + attr.getName().getLocalPart() + "=" +
                        // attr.getValue());
                    }
                    // System.out.println();
                }
                // 如果是正文
                if (event.isCharacters()) {
                    String s = event.asCharacters().getData();
                    if (null != s && s.trim().length() > 0 && titleFlag) {
                        System.out.println(s.trim());
                    }
                }
                // 如果事件对象是元素的结束
                if (event.isEndElement()) {
                    EndElement end = event.asEndElement();
                    String name = end.getName().getLocalPart();
                    if (name.equals("title")) {
                        titleFlag = false;
                    }
                }
            }
            eventReader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
    }
}

# 5.JSON 简介

  • JavaScript Object Notation, JS 对象表示法
  • 是一种轻量级的数据交换格式
  • 类似 XML,更小、更快、更易解析
  • 最早用于 Javascript 中,容易解析,最后推广到全语言
  • 尽管使用 Javascript 语法,但是独立于编程语言

# JSONObject 和 JSONArray

# Java 的 JSON 处理

  • org.json:JSON 官方推荐的解析类
    • 简单易用,通用性强
    • 复杂功能欠缺
  • GSON:Google 出品
    • 基于反射,可以实现 JSON 对象、JSON 字符串和 Java 对象互转
  • Jackson:号称最快的 JSON 处理器
    • 简单易用,社区更新和发布速度比较快

# Json 主要用途

  • JSON 生成
  • JSON 解析
  • JSON 校验
  • Java Bean 对象进行互解析
    • 具有一个无参的构造函数
    • 可以包括多个属性,所有属性都是 private
    • 每个属性都有相应的 Getter/Setter 方法
    • Java Bean 用于封装数据,又可称为 POJO (Plain Old Java Object)

# org.json

public static void main(String[] args) {
        testJsonObject();
        testJsonFile();
    }
    public static void testJsonObject() {
        // 构造对象
        Person p = new Person();
        p.setName("Tom");
        p.setAge(20);
        // 构造 JSONObject 对象
        JSONObject obj = new JSONObject();
        obj.put("name", p.getName());
        obj.put("age", p.getAge());
        System.out.println("name: " + obj.getString("name"));
        System.out.println("age: " + obj.getInt("age"));
    }
    public static void testJsonFile() {
        File file = new File("books.json");
        try (FileReader reader = new FileReader(file)) {
            // 读取文件内容到 JsonObject 对象中
            int fileLen = (int) file.length();
            char[] chars = new char[fileLen];
            reader.read(chars);
            String s = String.valueOf(chars);
            JSONObject jsonObject = new JSONObject(s);
            // 开始解析 JSONObject 对象
            JSONArray books = jsonObject.getJSONArray("books");
            List<Book> bookList = new ArrayList<>();
            for (Object book : books) {
                // 获取单个 JSONObject 对象
                JSONObject bookObject = (JSONObject) book;
                Book book1 = new Book();
                book1.setAuthor(bookObject.getString("author"));
                book1.setYear(bookObject.getString("year"));
                book1.setTitle(bookObject.getString("title"));
                book1.setPrice(bookObject.getInt("price"));
                book1.setCategory(bookObject.getString("category"));
                bookList.add(book1);
            }
            for (Book book : bookList) {
                System.out.println(book.getAuthor() + ",  " + book.getTitle());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

# GSON

public static void main(String[] args) {
        testJsonObject();
        testJsonFile();
    }
    public static void testJsonObject() {
        // 构造对象
        Person p = new Person();
        p.setName("Tom");
        p.setAge(20);
        // 从 Java 对象到 JSON 字符串
        Gson gson = new Gson();
        String s = gson.toJson(p);
        System.out.println(s); // {"name":"Tom","age":20}
        // 从 JSON 字符串到 Java 对象
        Person p2 = gson.fromJson(s, Person.class);
        System.out.println(p2.getName()); // Tom
        System.out.println(p2.getAge()); // 20
        // 调用 GSON 的 JsonObject
        JsonObject json = gson.toJsonTree(p).getAsJsonObject(); // 将整个 json 解析为一颗树
        System.out.println(json.get("name")); // "Tom"
        System.out.println(json.get("age")); // 20
    }
    public static void testJsonFile() {
        Gson gson = new Gson();
        File file = new File("books2.json");
        try (FileReader reader = new FileReader(file)) {
            List<Book> books = gson.fromJson(reader, new TypeToken<List<Book>>() {
            }.getType());
            for (Book book : books) {
                System.out.println(book.getAuthor() + ",  " + book.getTitle());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

# Jackson

public static void main(String[] args) throws Exception {
        testJsonObject();
        testJsonFile();
    }
    
    static void testJsonObject() throws IOException {
        ObjectMapper om = new ObjectMapper();
        
        // 构造对象
        Person p = new Person();
        p.setName("Tom");
        p.setAge(20);
        
        // 将对象解析为 json 字符串
        String jsonStr = om.writeValueAsString(p);
        System.out.println(jsonStr);
        
        // 从 json 字符串重构对象
        Person p2 = om.readValue(jsonStr, Person.class);
        System.out.println(p2.getName());
        System.out.println(p2.getAge());
        
        // 从 json 字符串重构为 JsonNode 对象
        JsonNode node = om.readTree(jsonStr);
        System.out.println(node.get("name").asText());
        System.out.println(node.get("age").asText());
    }
    
    static void testJsonFile() throws IOException {
        ObjectMapper om = new ObjectMapper();
        
        // 从 json 文件中加载,并重构为 java 对象
        File json2 = new File("books2.json");
        List<Book> books = om.readValue(json2, new TypeReference<List<Book>>(){});
        for (Book book : books) {
            System.out.println(book.getAuthor());
            System.out.println(book.getTitle());
        }
    }

# JSON vs XML

  • 都是数据交换格式,可读性强,可扩展性高
  • 大部分的情况下,JSON 更具优势(编码简单,转换方便)
  • JSON 字符长度一般小于 XML,传输效率更高
  • XML 更加注重标签和顺序
  • JSON 会丢失信息