以下内容转载和参考自:廖雪峰的官方网站。
1、XML
①、DOM方式解析XML
使用DOM是一次性读取XML,并在内存中表示为树形结构,其解析出来的树会增加一个根节点Document:
import java.io.IOException;
import java.io.InputStream;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
public class Main {
public static void main(String[] args){
InputStream input = Main.class.getResourceAsStream("book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try{
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input); //parse()可以接收InputStream,File或者URL
printNode(doc,0);
}catch (ParserConfigurationException ex) {
//newDocumentBuilder() error
System.out.println(ex.getMessage());
}catch (IOException | SAXException | IllegalArgumentException ex){
//parse() error: IO错误、解析错误、参数为NULL
System.out.println(ex.getMessage());
}
}
public static void printNode(Node n, int indent) {
for (int i = 0; i < indent; i++) {
System.out.print(" ");
}
switch (n.getNodeType()) {
case Node.DOCUMENT_NODE: // Document根节点
System.out.println("Document: " + n.getNodeName());
break;
case Node.ELEMENT_NODE: // 元素节点
System.out.println("Element: " + n.getNodeName());
break;
case Node.TEXT_NODE: // 文本
System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
break;
case Node.ATTRIBUTE_NODE: // 属性
System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
break;
default: // 其他
System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
}
for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
printNode(child, indent + 1);
}
}
}
②、使用SAX解析XML
SAX是一种基于流的解析方式,边读取XML边解析,并以事件回调的方式让调用者获取数据。因为是一边读一边解析,所以无论XML有多大,占用的内存都很小。
SAX解析会触发以下的一系列事件,所以我们需要给SAX传入一个继承自DefaultHandler的回调对象,具体使用SAX的相关可以参考廖雪峰的官方网站 。
- startDocument:开始读取XML文档;
- startElement:读取到了一个元素,例如
<book>
; - characters:读取到了字符;
- endElement:读取到了一个结束的元素,例如
</book>
; - endDocument:读取XML文档结束。
③、使用Jackson
如下所示的XML的结构,它完全可以对应到一个定义好的JavaBean中,使用开源库Jackson可以轻松做到XML到JavaBean的转换。使用Jackson将XML转换到JavaBean中的详见廖雪峰的官方网站。
<?xml version="1.0" encoding="UTF-8" ?>
<book id="1">
<name>Java核心技术</name>
<author>Cay S. Horstmann</author>
<tags>
<tag>Java</tag>
<tag>Network</tag>
</tags>
</book>
public class Book {
public long id;
public String name;
public String author;
public List<String> tags;
}
2、JSON
JSON中大括号及其内容称为对象(object),其包含一个或多个元素,元素又称属性,每个元素用逗号分隔。
元素是键值对类型的,元素的“键”必须为字符串,元素的“值”可以为数字、字符串、逻辑值(true/false)、null,也可以为对象或对象数组。
JSON规范中不支持注释,使用json-schema或JSON5的话支持。
下面是使用Jackson来进行JSON和JavaBean的相互转换的示例:
public class TestJackson {
public static void main(String[] args) throws IOException {
Map<String,Object> params = new HashMap<>();
params.put("name","jack");
params.put("age",18);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES/*如果JavaBean不存在该属性时解析不会报错*/, false); //反序列化(JSON 2 JavaBean)时忽略在 json 中存在但 Java 对象不存在的属性
ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //在序列化(JavaBean 2 JSON)时忽略值为 null 的属性
//将对象转为JSON串
String jsonString = objectMapper.writeValueAsString(params);
System.out.println(jsonString);
//将JSON串 转为 Object 对象
Map resultMap = objectMapper.readValue(jsonString, HashMap.class);
resultMap.entrySet().stream().forEach(entry ->{
System.out.println(entry);
});
}
}
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
class Book{
public float num;
public boolean flag;
public HashMap name;
public List<String> tags;
public List<HashMap> names;
}
public class Main {
public static void main(String[] argc) throws Exception{
InputStream input = Main.class.getResourceAsStream("book.json");
ObjectMapper mapper = new ObjectMapper();
Book book = mapper.readValue(input, Book.class); //反序列化:JSON 2 JavaBean
String json = mapper.writeValueAsString(book); //序列化:JavaBean 2 JSON
json = mapper.writeValueAsString(List.of(book)); //序列化:支持将JavaBean的集合转换为JSON
}
}
在序列化的时候,如果JavaBean中的成员没有被初始化或者设置值的话,会将该属性的值序列化为null。对于List会当做一个元素(key为List名,value为数组),对于map成员也会当做一个元素(key为map的名称,value为一个对象,对象中的元素为map中的成员):
要把JSON的某些值解析为指定的类型的话,需要提供一个转换方法,如将"978-7-111-54742-6"转换为BigInteger类型:
Jackson会将枚举类型序列化为String类型:
其它注意事项:
在反序列化时,Jackson要求Java类需要一个默认的无参数构造方法,否则,无法直接实例化此类。所以对于存在带参数构造方法的类,如果要反序列化,注意要再提供一个无参数构造方法。
对于record类型(java 14),Jackson会自动找出它的带参数构造方法,并根据JSON的key进行匹配,可直接反序列化。对record类型的支持需要版本2.12.0以上。