参考博客:https://blog.csdn.net/gavin_john/article/details/51511180
xml(eXtensible Markup Language)可扩展标记语言,它具有平台无关性,是一门独立的标记语言。不论是Pyhton、Java还是C语言,都可以解析xml的数据或者生成xml,这一特性是xml可以作为程序之间通信介质的重要前提。
xml可以以多种形式保存,.xml文件只是其中的一种,我们还可以在内存中构建xml数据。
发展历史:
下面是一个xml格式的一个简单例子:
<class>
<stu id="001">
<name>张三name>
<gender>男gender>
<age>18age>
stu>
<stu id="002">
<name>小红name>
<gender>女gender>
<age>16age>
stu>
class>
一个xml文件包含以下几部分内容:
- 文档声明
- 命名空间(可选)
- 元素
- (元素的)属性
- 注释
- CDATA区、特殊字符
- 处理指令(processing instruction)
文档声明
包含version(文档符合的xml版本号),encoding(文档的字符编码)。
元素(标签/节点/标记)
<元素名>标签体元素名>
元素名>
<stu>小明stu>
<stu>
小明
stu>
元素命名规范:
- 名称可以含字母、数字以及其他的字符
- 大小写敏感,例如,元素P和元素p是两个不同的元素
- 不能以数字或下划线”_”开头
- 元素内不能包含空格
- 名称中间不能包含冒号(:)
- 可以使用中文,但一般不这么用
- 名称不能以字符"xml"(或者XML、Xml)开始
属性
<元素名 属性名1="属性值1" 属性名2="属性值2">元素名>
<元素名>
<属性名1 value="属性值1"/>
<属性名2 value="属性值2"/>
元素名>
属性值用双引号(”)或单引号(’)分隔,如果属性值中有单引号,则用双引号分隔;如果有双引号,则用单引号分隔。那么如果属性值中既有单引号还有双引号怎么办?这种要使用实体(转义字符,类似于html中的空格符),XML有5个预定义的实体字符,如下:
实体字符 | 代表字符 | 含义 |
---|---|---|
< |
< | 小于 |
> |
> | 大于 |
& |
& | 和 |
' |
' | 单引号 |
" |
" | 双引号 |
CDATA节
有些内容可能不想让解析引擎解析执行,而是当做原始内容处理,用于把整段文本解释为纯字符数据而不是标记。这就要用到CDATA节
<stu id="001">
<name>张三name>
<gender>男gender>
<age>18age>
<intro>intro>
stu>
处理指令
在同一文件夹目录中创建以下两个文件:
my.css:
name{
font-size:80px;
font-weight:bold;
color:red;
}
gender{
font-size:60px;
font-weight:bold;
color:blue;
}
age{
font-size:40px;
font-weight:bold;
color:green;
}
my.xml(在xml中引用my.css):
<class>
<stu id="001">
<name>张三name>
<gender>男gender>
<age>18age>
stu>
<stu id="002">
<name>小红name>
<gender>女gender>
<age>16age>
stu>
class>
然后使用浏览器打开,发现已经套上了css的样式(如果发现乱码请改变xml文件的字符集):
PS:如果出现乱码会是这样的
命名空间
DOCTYPE mapper SYSTEM "http://mybatis.org/dtd/mybatis-3-mapper.dtd" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN">
<mapper namespace="com.sketch.vueblog.mapper.MBlogMapper"> mapper>
mybatis的mapper、Spring的配置文件、maven的pom文件都需要在文件头写入命名空间,它可以提供一定的标签语法检查(这样有时候就不能自定义标签了)
导入dom4j包(同时还要导入jaxen包,因为dom4j有很多东西是依赖jaxen的)
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
students.xml
<class>
<stu id="001" gender="男">
<rank value="2"/>
<name>张三name>
<age>18age>
stu>
<stu id="002" gender="女">
<rank value="1"/>
<name>小红name>
<age>16age>
stu>
class>
private Element getRootElementFromFile(String filePath) throws FileNotFoundException, DocumentException {
// 创建一个指向XML文件的输入流
FileInputStream fis = new FileInputStream(filePath);
// 创建一个XML读取工具对象
SAXReader sr = new SAXReader();
// DOMReader dr = new DOMReader(); // 也可以创建DOMReader(区别有待探究)
Document doc = sr.read(fis);
// 获取根节点
Element root = doc.getRootElement();
return root;
}
private void test1() {
try {
Element root = getRootElementFromFile("students.xml");
// 获取根节点名称
String rootName = root.getName();
System.out.println(rootName);
// 获取根节点列表
List<Element> elements = root.elements();
for (Element element : elements) {
// 获取根节点属性
String id = element.attributeValue("id");
String gender = element.attributeValue("gender");
// 获取子节点内容
String name = element.element("name").getText();
String age = element.element("age").getText();
System.out.println(name + "-" + id + "-" + gender + "-" + age);
}
} catch (FileNotFoundException | DocumentException e) {
e.printStackTrace();
}
}
从url资源读取xml
private Element getRootElementFromURL(String urlStr) throws IOException, DocumentException {
// 获取XML资源输入流
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
SAXReader sr = new SAXReader();
Document doc = sr.read(is);
// Document doc = sr.read(url);
Element root = doc.getRootElement();
return root;
}
private void test2() {
try {
String phone = "17727478831";
Element root = getRootElementFromURL("http://apis.juhe.cn/mobile/get?phone="+ phone + "&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
String code = root.elementText("resultcode");
Node node = root.selectSingleNode("//company");
System.out.println(node.getName() + ":" + node.getText());
if ("200".equals(code)) {
Element result = root.element("result");
String province = result.elementText("province");
String city = result.elementText("city");
if (province.equals(city)) {
System.out.println("手机号码归属地为:" + city);
} else {
System.out.println("手机号码归属地为:" + province + city);
}
} else {
System.out.println("请输入正确的手机号码");
}
// System.out.println("手机运营商为:" + node.getText());
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
Xpath语法
我们注意到上述代码中有一个方法
Node node = root.selectSingleNode("//company");
该方法可以在xml中层序遍历找到company标签,这里的"//company"
就是Xpath语法
其用法详细总结可以参考:https://blog.csdn.net/u013332124/article/details/80621638
基本语法:
表达式 描述 nodename 选取此节点的所有子节点。 / 从根节点选取。 // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 . 选取当前节点。 … 选取当前节点的父节点。 @ 选取属性。 示例:
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()❤️] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。
JSON官方文档:http://www.json.org/json-zh.html
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使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字符串再传输
JSON 的语法规则十分简单,可称得上“优雅完美”,总结起来有:
数组(Array)用方括号(“[]”)表示。
对象(Object)用大括号(”{}”)表示。
名称/值对(name/value)组合成数组和对象。
名称(name)置于双引号中,值(value)有字符串、数值、布尔值、null、对象和数组。
并列的数据之间用逗号(“,”)分隔
其中对象和数组还可以互相嵌套
// 正确的json格式(PS:如果是字符串,不论是键还是值最好都用双引号括起来,否则有可能不能通过校验)
{
"password": 123456,
"name": "myname",
"Booleans": true,
"Array": [{"name": "cxd"},"y", "z"],
"object": {}
}
json字符串还可以压缩、转义,这样更加方便以字符串形式传输
{\"password\":123456,\"name\":\"myname\",\"Booleans\":true,\"Array\":[{\"name\":\"cxd\"},\"y\",\"z\"],\"object\":{}}
导入fastjson包
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.75version>
dependency>
json的生成和解析都超级超级方便
在fastjson中,点开JSON这个类的介绍我们可以看到
This is the main class for using Fastjson. You usually call these two methods toJSONString(Object) and parseObject(String, Class).
Here is an example of how fastjson is used for a simple Class:
Model model = new Model();
String json = JSON.toJSONString(model); // serializes model to Json
Model model2 = JSON.parseObject(json, Model.class); // deserializes json into model2
这便是fastjson最核心也是最常用的用法,首先我们编写以下测试用的Java类
请注意!!!对象的属性必须设为public或必须居有get和set方法,否则不能正常解析!
// 构造方法、get/set方法和toString方法省略了(均为内部类)
class Student {
String name;
int age;
List<Subject> subjects;
}
class Subject {
String name;
int score;
}
// 将对象转为json字符串
List<Subject> subjects = Arrays.asList(
new Subject("语文", 88),
new Subject("英语", 87));
Student stu = new Student("cxd", 18, subjects);
String jsonStr = JSON.toJSONString(stu);
System.out.println(jsonStr);
运行结果:
{"age":18,"name":"cxd","subjects":[{"name":"语文","score":88},{"name":"英语","score":87}]}
让我们试着再把这个字符串转回Java对象,遇到如下问题↓
String jsonStr = "{\"age\":18,\"name\":\"cxd\",\"subjects\":[{\"name\":\"语文\",\"score\":88},{\"name\":\"英语\",\"score\":87}]}";
JSONObject jsonObj = JSON.parseObject(jsonStr);
Student stu = jsonObj.toJavaObject(Student.class);
原因未知,作出以下尝试时又遇到问题二↓
我怀疑可能是对象Student中的List又嵌套了一个自定义类Subject,导致类型转换什么的出错,所以我又重新写了两个类
class Student2 {
public String name;
public int age;
public HomeTown ht;
}
class HomeTown {
public String name;
}
// 测试代码
Student2 stu = new Student2();
HomeTown ht = new HomeTown();
ht.name = "广东";
stu.age = 18; stu.name = "cxd"; stu.ht = ht;
String jsonStr = JSON.toJSONString(stu);
Student2 stu2 = JSON.parseObject(jsonStr, Student2.class);
System.out.println(stu2.name + stu2.age + stu2.ht.name);
抛出如下错误,大概意思是不能创建非静态的内部类,于是在Student2和HomeTown两个类前添加static修饰,问题解决
经过以上尝试后,我在Student和Subject类前也加了static修饰符,但又抛出了如下异常
Exception in thread “main” com.alibaba.fastjson.JSONException: create instance error, public com.cxd.json.JSONTest S t u d e n t ( j a v a . l a n g . S t r i n g , i n t , j a v a . u t i l . L i s t < c o m . c x d . j s o n . J S O N T e s t Student(java.lang.String,int,java.util.List
Student(java.lang.String,int,java.util.List<com.cxd.json.JSONTest Subject>)
at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:1551)
at com.alibaba.fastjson.JSONObject.toJavaObject(JSONObject.java:614)
at com.cxd.json.JSONTest.test2(JSONTest.java:115)
at com.cxd.json.JSONTest.main(JSONTest.java:13)
Caused by: com.alibaba.fastjson.JSONException: create instance error, public com.cxd.json.JSONTest S t u d e n t ( j a v a . l a n g . S t r i n g , i n t , j a v a . u t i l . L i s t < c o m . c x d . j s o n . J S O N T e s t Student(java.lang.String,int,java.util.ListStudent(java.lang.String,int,java.util.List<com.cxd.json.JSONTest Subject>)
at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:1585)
at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:1549)
… 3 more
Caused by: java.lang.IllegalAccessException: Class com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer can not access a member of class com.cxd.json.JSONTest$Student with modifiers “public”
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:1582)
… 4 more
最后我将两个内部类迁移独立出来,重新编写成Student.java和Subject.java,问题解决~
JSONObject和JSONArray是JSON的两个子类
下面是我写过的在一个长JSON串中递归寻找一个JSONArray(名称为"subjects")的代码
private JSONArray getSubjects(JSONObject jObj) {
if (jObj.entrySet() == null || jObj.entrySet().size() == 0) {
return null;
}
for (Map.Entry<String, Object> jsonEntry : jObj.entrySet()) {
if (jsonEntry.getValue() instanceof JSONArray && jsonEntry.getKey().equals("subjects")) {
return (JSONArray) jsonEntry.getValue();
} else if (jsonEntry.getValue() instanceof JSONObject){
JSONArray temp = null;
if ((temp = getSubjects((JSONObject) jsonEntry.getValue())) != null) {
return temp;
}
}
}
return null;
}
其实我们就可以把JSON理解成只有JSONObject(ObjectMap,键值对)和JSONArray(ObjectArray,对象数组)两种数据形式。JSONObject继承了Map