【Java工具】XML文件与Properites文件解析工具

  XML文件与Properties文件在我的Java学习中和以后要接触的框架中十分常见的文件类型。这些文件懂了相关的编写规则后会十分容易写出来,但是作为配置文件如何去用它才是关键。人根据自己要完成的目的编写相关文件,可是你把这个文件放在计算机面前它不懂啊,它只知道01序列。因此,对于这种高频出现的文件,我们有必要编程,编写一个工具来解析它,方便以后使用。我不是直接把相关解析工具代码直接贴出来,而是一步步写关于这个工具如何造出来,这样才能学习更深刻并且里面蕴含的工具思想也能写出来。

XML文件解析工具

初识XML

  XML(Extensible Markup Language),中文翻译为可扩展标记语言。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。(以上摘自百度百科)使用它我们可以很容易的描述复杂的层次数据结构。例如,下面给的一个XML文件简单例子,学生基本信息。

<?xml version="1.0" encoding="UTF-8"?>
<students>
	<student name= "张三" id= "20203035" sex= "">
		<hobbies>
			<hobby>打游戏</hobby>
			<hobby>篮球</hobby>
			<hobby>学习</hobby>
		</hobbies>
	</student>
	<student name= "李四" id= "20203036" sex= "">
		<hobbies>
			<hobby>足球</hobby>
			<hobby>写作</hobby>
			<hobby>摄影</hobby>
		</hobbies>
	</student>
	<student name= "王五" id= "20203037" sex= "">
		<hobbies>
			<hobby>购物</hobby>
			<hobby>逛街</hobby>
			<hobby>羽毛球</hobby>
		</hobbies>
	</student>
</students>

  只是个简单的学生信息XML文件。对XML文件稍有了解,知道<></>是标签,标签里面带着的叫做属性。分析上面XML文件知,根标签是students(根标签必须有),下一个标签student,其中有三个属性(name, id, sex),下一个标签是hobbies,然后下一个标签是hobby。看到这个XML文件有没有觉得它特别像树形结构。因此,我们使用四种XML解析方式之一的DOM解析。

public class Test {

	public static void main(String[] args) {

		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
			InputStream is = Test.class.getResourceAsStream("/studentInfo.xml");
			//这里是用当前类的class.getResourceAsStream。参数是相对路径。
			Document document = documentBuilder.parse(is);
			//前面四句话是不是看的一头雾水?我最初学也是,这里没必要刨根问底,当作巫师的咒语吧!
			
			NodeList studentList = document.getElementsByTagName("student");
            //根据标签名得到一个结点列表
			for (int i = 0; i < studentList.getLength(); i++) {
				StudentInfo stu = new StudentInfo();	
                //StudentInfo类只是为了最后显示好看。
				Element student = (Element) studentList.item(i);
                //要把Node类型强转为Element类型
				String name = student.getAttribute("name");
				String id = student.getAttribute("id");
				String sex = student.getAttribute("sex");
				stu.setId(id);
				stu.setSex(sex);
				stu.setName(name);
				
				NodeList hobbyList = student.getElementsByTagName("hobby");
				for (int j = 0; j < hobbyList.getLength(); j++) {
					String hobbyString = hobbyList.item(j).getTextContent();
					stu.addHobby(hobbyString);
				}
				System.out.println(stu);
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

要用到的model类

public class StudentInfo {

	private String id;
	private String name;
	private String sex;
	private List<String> hobbies;
	
	public StudentInfo() {
		hobbies = new ArrayList<>();
	}

	public String getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

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

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public List<String> getHobbies() {
		return hobbies;
	}

	public void addHobby(String hobby) {
		if (hobby == null) {
			return;
		}
		if (hobbies.contains(hobby)) {
			return;
		}
		hobbies.add(hobby);
	}
	
	@Override
	public String toString() {
		StringBuffer stu = new StringBuffer();
		stu.append(name).append(" ")
			.append(id).append(" ")
			.append(sex).append(" ");
		
		for (String hobby : hobbies) {
			stu.append("\n").append("\t").append(hobby);
		}
		
		return stu.toString();
	}
	
}

运行结果

张三 20203035 男 
	打游戏
	篮球
	学习
李四 20203036 男 
	足球
	写作
	摄影
王五 20203037 女 
	购物
	逛街
	羽毛球

  关于NodeList,Node,Element这几个类我需要说一下。

​  getElementsByTagName(“标签名”)获得的是个NodeList,没办法强转成Element。但可以遍历这个NodeList,item(index)方法取得相应的一个Node。Node与Element之间有关系。

​  对于一个出现多次的标签(student),我们获得一个NodeList,遍历它,如果又遇到出现多次的标签(hobby),再获得一个NodeList,遍历它,如此下去,直到所有标签都被过一遍。

  总结:NodeList是标签多次出现组成,最终要访问到具体内容还是要提供Element,NodeList与Element之间转换通过Node完成。
三者关系映像图

解析XML工具

  所谓工具就是工具制造者把那些重复的、一成不变的代码提取出来做成工具,而那些会变部分的代码向外提供,由工具使用者去实现。就比如现在给你的不是这个学生信息XML表了,而换了另一个XML让你解析,再看解析代码,发现for循环上面的都是一成不变的(除了参数),而底下的真正解析过程会随着不同XML变化。

​  以后处理XML文件,在结构上任何XML文件都是相同的。只是在细节上标签名或者属性名有所差别而已。相同之处做成工具,造工具的人只编写XML相同的,结构相似的部分;差别之处拎出来提供给外面,让工具使用者去完成。

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public abstract class XMLParse {

	private static final DocumentBuilderFactory dbf;
	private static DocumentBuilder db;
	
	static {
		dbf = DocumentBuilderFactory.newInstance();
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}
	
	public XMLParse() {
	}
	
	public static Document getDocument(InputStream is) {
		Document document = null;
		try {
			document = db.parse(is);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return document;
	}
	
	public static Document getDocument(String xmlPath) {
		InputStream is = XMLParse.class.getResourceAsStream(xmlPath);
		if (is == null) {
			System.out.println("xmlPath[" +xmlPath + "不存在");
			return null;
		}
		return getDocument(is);
	}
	
	/*getElementsByTagName写了两次,但两次前缀不一样,一个是doucument,一个是element类型的,覆盖就行了*/
	public void parseTag(Document document, String tagName) {
		NodeList nodeList = document.getElementsByTagName(tagName);
		for (int index = 0; index < nodeList.getLength(); index++) {	
			Element element = (Element) nodeList.item(index);	
            //底下的内容不同的xml解析不尽相同,工具写到这没法写了。
			dealElement(element, index);
		}
	}

	public void parseTag(Element element, String tagName) {
		NodeList nodeList = element.getElementsByTagName(tagName);
		for (int index = 0; index < nodeList.getLength(); index++) {	
			Element ele = (Element) nodeList.item(index);	
            //底下的内容不同的xml解析不尽相同,工具写到这没法写了。
			//后面对ele进一步解析:取属性,取textContent或进一步取Element都应该是未来的工具使用者操心的事,我要做的仅仅是是把ele交给对方
			dealElement(ele, index);
		}
	}
	
	public abstract void dealElement(Element element, int index);	
    //这个index让用户知道处理到第几项都能知道

}

  XML文件解析工具就写出来了。还是那句话将那些已经确定了的代码写好,对于以后操作者如何去用我不管,我也管不着,所以将它传给外面,尽量将有用的信息都传出去,因为这样使用工具的人才能多一些机会做自己的操作。

  如何去用这个工具下面给个例子。

public class LocalTest {

	public static void main(String[] args) {

		new XMLParse() {		//直接用匿名内部类去使用,方便快捷。
			
			@Override
			public void dealElement(Element element, int index) {//循环都不用写了,因为这句话就在循环里
				StudentInfo stu = new StudentInfo();
				String name = element.getAttribute("name");	//此时这个element就是标签student
				String id = element.getAttribute("id");
				String sex = element.getAttribute("sex");
				stu.setId(id);
				stu.setSex(sex);
				stu.setName(name);
				new XMLParse() {
					
					@Override
					public void dealElement(Element element, int index) {
                        //始终记得的这个是在循环里
						//可直接再使用匿名内部类解析下一个标签。也可以新建方法解析把这层提出去,这样可读性更强,且不会造成代码逻辑混乱。
						String hobbyString = element.getTextContent();
						stu.addHobby(hobbyString);
					}
				}.parseTag(element, "hobby");
				System.out.println(stu);
			}
		}.parseTag(XMLParse.getDocument("/studentInfo.xml"), "student");
	}

}

Properties文件解析工具

  Properties文件就十分简单了,它的格式主要是以key-value键值对的形式存储,所以它的工具类就十分好建立了。既然是个键值对,就放在Map数据结构中,要遵循Map的规则(键值最好不要重复)。

​  下面给个简单的properties文件例子,该例子是我连接数据库的相关配置。

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mec_javase_201910?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
user=root
password=123456

  其工具类因为它是键值对形式,所以放在一个大池子里,解析一个就放进去一个,想用就从池子取。

public class PropertiesParse {

	private static final Map<String, String> pool;
	
	static {
		pool = new HashMap<>();
	}
	
	private static void loadding(InputStream is) {
		
		Properties properties = new Properties();
		try {
			InputStreamReader isr = new InputStreamReader(is, "utf-8");
			/*乱码问题很有可能是输入流为其他格式的流的原因,解决方法强制转为utf-8流。*/
			properties.load(isr);
			Enumeration<Object> keys = properties.keys();
			
			while (keys.hasMoreElements()) {
				String key = (String) keys.nextElement();
				String value = properties.getProperty(key);
				pool.put(key, value);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void loadCfgPath(String path) {
		InputStream is = PropertiesParse.class.getResourceAsStream(path);
		if (is == null) {
			System.out.println("Path[" +path + "不存在");
			return;
		}
		loadding(is);
	}
	
	public static void loadCfgAbsolutePath(String path) {
		InputStream is = null;
		try {
			is = new BufferedInputStream(new FileInputStream(path));
			loadding(is);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static String value(String key) {
		return pool.get(key);
	}
	
	public static Set<String> getKeySet() {
		return pool.keySet();
	}
	
}

工具使用演示

public class PoropertiesTest {

	public static void main(String[] args) {

		ProperitiesParse.loadProperities("/connection.properties");
		System.out.println(ProperitiesParse.value("driver"));
		System.out.println(ProperitiesParse.value("url"));
		System.out.println(ProperitiesParse.value("user"));
		System.out.println(ProperitiesParse.value("password"));
	}

}


/*
演示结果
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/mec_javase_201910?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
root
123456
*/

总结

  至此,两个常用文件的简单解析工具就完成了。XML解析工具工具思想多一些,可以着重看也可以跟着敲一遍,才能收获更深。

XMLParse工具的补充

  在实际使用这个工具的时候,我遇到过这样一种XML文件。

<?xml version="1.0" encoding="UTF-8"?>
<menu_bar>
	<Separator></Separator>
	<Separator></Separator>
	<Separator></Separator>
	<Separator></Separator>
	<menuItem caption="exit"></menuItem>
	<menu caption= "File">
		<menu caption="New">
			<menuItem caption = "Class"></menuItem>
			<menuItem caption = "Package"></menuItem>
			<menuItem caption = "Interface"></menuItem>
		</menu>
		<menuItem caption="Save"></menuItem>
		<Separator></Separator>
		<menuItem caption="Exit"></menuItem>
	</menu>
	<menu caption= "Edit">
		<menuItem caption="Copy"></menuItem>
		<menuItem caption="Cut"></menuItem>
		<menuItem caption="Paste"></menuItem>
	</menu>
	<menu caption= "Format">
		<menu caption="Font">
			<menu caption="MicroSoft">
				<menuItem caption="MFour"></menuItem>
				<menuItem caption="MFive"></menuItem>
			</menu>
			<menu caption="Song">
				<menuItem caption="SFour"></menuItem>
				<menuItem caption="SFive"></menuItem>
			</menu>
		</menu>
		<menuItem caption="AutoNewLine"></menuItem>
	</menu>
	<menu caption= "Option">
		<menuItem caption="AddAFrame"></menuItem>
	</menu>
	<menu caption="Help">
		<menuItem caption="About"></menuItem>
	</menu>
</menu_bar>

  我们可以看到menu标签,里面还有menu标签。如果用我们上面的工具以标签名进行解析的,那就不正确了。因此解析XML文件不仅要可以按标签名称解析,还需要可以按层次解析。

修改工具如下

public abstract class XMLParse {

	private static final DocumentBuilderFactory dbf;
	private static DocumentBuilder db;
	
	
	static {
		dbf = DocumentBuilderFactory.newInstance();
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}
	
	public XMLParse() {
	}
	
	public static Document getDocument(InputStream is) {
		Document document = null;
		try {
			document = db.parse(is);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return document;
	}
	
	public static Document getDocument(String xmlPath) {
		InputStream is = XMLParse.class.getResourceAsStream(xmlPath);
		if (is == null) {
			System.out.println("xmlPath[" +xmlPath + "不存在");
			return null;
		}
		return getDocument(is);
	}
	
	public static Document getDocumentByAbsolutePath(String xmlPath) {
		InputStream is = null;
		try {
			is = new BufferedInputStream(new FileInputStream(xmlPath));
			return getDocument(is);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public void parseTag(Document document, String tagName) {
		NodeList nodeList = document.getElementsByTagName(tagName);
		for (int i = 0; i < nodeList.getLength(); i++) {
			Element element = (Element) nodeList.item(i);
			dealElement(element, i);
		}
	}
	
	public void parseTag(Element element, String tagName) {
		NodeList nodeList = element.getElementsByTagName(tagName);
		for (int i = 0; i < nodeList.getLength(); i++) {
			Element ele = (Element) nodeList.item(i);
			dealElement(ele, i);
		}
	}
	
	public abstract void dealElement(Element element, int index);
	
	public void parseRoot(Document document) {	//开始从根标签开始解析
		Element root = (Element) document.getChildNodes().item(0);
		dealElement(root, 0);
	}
	
	public void parseElement(Element element) {	//从根解析后才可以按层解析
		NodeList nodeList = element.getChildNodes();
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node node = nodeList.item(i);
			if (node instanceof Element) {	 //为什么有这个?因为确保他是element类型就是一行,输出还有其他东西那些不要。
				dealElement((Element) node, i);
			}
		}
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值