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
- 名称 / 值对。如 "firstName":"John"
- JSON 对象:
- 数据在键值对中
- 数据由逗号分隔
- 花括号保存对象
- JSON 数组
- 方括号保存数组
[{“name":"Jo","email":"a@b.com"}, {“name":"Jo","email":"a@b.com"}]
- 方括号保存数组
# 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 会丢失信息