本博客講的XML解析,使用的是dom4j。
首先建立一個maven項目,在dom.xml中引入相應的dom4j的版本。作者下載的是熱度很高的1.6.1版本。maven的使用在這里不做詳細講解。
引入成功后,來簡單了解該包提供的API
1.org.dom4j.io.SAXReader.class-----該類提供了reader方法,可以將xml文件讀取為Document對象,該方法返回值類型為Document
2.org.dom4j.Document.class----------該類提供了getRootElement方法,可以獲得Document對象的根節點,此方法返回值類型是Element
3.org.dom4j.Element-------該類提供了elements方法,獲取所有的子節點,返回值類型為Element;
attributeValue方法,獲得通過屬性名節點的屬性值,返回值類型為String
getStringValue()方法,獲得element的文本值,返回值類型為String
下面使用上述方法來解析XML文件。
一:新建或者自己導如XML文件。作者為了方便演示,新建一個新的XML文件,如下。
<?xml version="1.0" encoding="UTF-8"?> <students> <student id="1"> <name>Claire</name> <age>18</age> <gender>女</gender> </student> <student id="2"> <name>Leafly</name> <age>18</age> <gender>男</gender> </student> <student id="3"> <name>Dingdang</name> <age>18</age> <gender>男</gender> </student> <student id="4"> <name>DingDing</name> <age>18</age> <gender>男</gender> </student> <student id="5"> <name>DangDang</name> <age>18</age> <gender>女</gender> </student> </students>
2.將上述xml文件放到src下面。
3.創建一個Student類,其屬性與上述xml中的元素一致。
public class Student { //private static final int AAA = 1; private String id; private String name; private String age; private String gender; //構造 public Student() { } public Student(String id, String name, String age, String gender) { super(); this.id = id; this.name = name; this.age = age; this.gender = gender; } //getter setter 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 getAge() { return age; } public void setAge(String age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } //toString @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]"; } }
4.創建新類ReadXMl,在該類中創建方法來解析XML文件。
private static void readXmlFun() throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { //1.反射,得到類的引用 Class student = Class.forName("readXmlAndReflect.Student"); //通過類的引用,得到類的對象 Object stuInstance = student.newInstance(); //創建一個list 來放多個student的對象 List<Student> students = new ArrayList<Student>(); SAXReader reader = new SAXReader(); //將XML文件讀取為一份document對象 Document document = reader.read(readXmlFun.class.getResourceAsStream("/StudentDate.xml")); //利用Document類中的方法,獲取根節點.返回的是Element Element rootElement = document.getRootElement(); //利用Element中的方法,獲取根節點下的全部子節點.返回一個List<element> List<Element> elements = rootElement.elements(); //利用Element中的方法,獲取子 節點中的屬性(StudentData中的屬性為id) //1.遍歷list,獲得每個元素 for (Element element : elements) { System.out.println("---------------------------------"); //遍歷並得到每個元素執行屬性名稱的屬性值 String stuId = element.attributeValue("id"); System.out.println("學生id為"+ stuId); //遍歷並獲得每個元素的全部子節點,返回一個List<element> List<Element> subElement = element.elements(); //通過方法名反射出方法對象 Method method2 = student.getDeclaredMethod("setId", String.class); //通過反射調用方法,stuInstance對象調用method,參數為stuData---相當於給各參數賦值 method2.invoke(stuInstance, stuId); for (Element subElementData : subElement) { //得到每個子節點的名字 String elementName = subElementData.getName(); //遍歷並獲得每個子元素的文本內容,如得到name子節點的文本值為Claire String stuData = subElementData.getStringValue(); System.out.println(elementName +"為" + stuData); //通過elemetname得到對應的get set方法,先拼接出方法名,比如 name--setName String funName = "set" + (elementName.charAt(0)+"").toUpperCase()+elementName.substring(1); //通過方法名反射出方法對象 Method method1 = student.getDeclaredMethod(funName, String.class); //通過反射調用方法,調用stuInstance對象的method方法,參數為stuData---給各屬性賦值 method1.invoke(stuInstance, stuData); } //將每個學生對象添加到list列表中 students.add((Student)stuInstance); System.out.println(students); } }
這里解釋下,上面使用的
Document document = reader.read(readXmlFun.class.getResourceAsStream("/StudentDate.xml"));
前面加上/------------代表在當前類的src下面找
如果沒有/--------------代表在跟類同級的目錄下找。
可以看到上面使用了反射。利用反射可以動態的為每一個Student對象賦值。當我們的XML文件改變,添加或刪除了新的屬性之后,完全不需要去修改我們的ReadXML類,只在Student類中添加或刪除對應的屬性即可。實現了解耦。
我們經常通過反射來做下面的事情:
1.獲取到某各類的引用
通常有3種方式:
1.Class clazz = Class.forName(" 這里必須是類全名");
2.我們每一個類都有一個靜態的class屬性:Class clazz =類名A.Class(這個與第一種方式的區別是,不會去運行類A中的靜態代碼塊);
3.每個類都有一個getClass()方法: classA a = new ClassA(); Class clazz =a.getClass();
2.通過該類的引用獲取類的對象
object o = clazz.newInstance();
3.通過該類的引用獲取屬性
4.通過該類的引用獲取所有方法(普通/構造)
下面代碼詳細說明了反射機制中常用的方法,有興趣可以了解下:
package readXmlAndReflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.Types; public class Reflect { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Class clazz = Class.forName("readXmlAndReflect.Student"); //創建對象 Object o =clazz.newInstance(); //創建一個stringBuffer StringBuffer buffer = new StringBuffer(); //類 public class readXmlAndReflect.Student{} buffer.append("\n"+"-------------class-------------------"+"\n"); buffer.append(Modifier.toString(clazz.getModifiers()) + " class " + clazz.getName()+"{"+"\n"); //域 buffer.append("\n"+"-------------field-------------------"+"\n"); //獲取所有聲明的屬性,包括私有和常量 Field[] fields = clazz.getDeclaredFields(); //遍歷所有的屬性 for (Field field : fields) { //獲取屬性的修飾符 如public static buffer.append("\t" + Modifier.toString(field.getModifiers())); //獲取屬性的類型 如 string int getType()獲取到的類全名(如java.lang.String),想要獲得簡單類型名如String,可以使用Class類中getSimpleName()方法 buffer.append("\t" + field.getType().getSimpleName()); //獲取屬性名稱 buffer.append("\t" + field.getName()); buffer.append(";"+"\n"); //得到域之后有什么用呢? //我們可以直接使用得到的域給其賦值或讀取域的值,由於域都是私有的,這里需要利用反射打破類的封裝 field.setAccessible(true);//如果沒有這一句,運行時會報錯java.lang.IllegalAccessException: //給對象0的域field 賦值為"3" field.set(o, "3"); //獲取 對象o 的 域field 的值 System.out.println(field.get(o)); } //方法 如 public static string function(){} buffer.append("\n"+"-------------function-------------------"+"\n"); //獲得所有聲明的方法,包括private Method[] motheds = clazz.getDeclaredMethods(); for (Method method : motheds) { //獲得方法的修飾符 如 public static buffer.append("\t"+Modifier.toString(method.getModifiers()) + " "); //獲得方法的返回值類型 buffer.append(method.getReturnType().getSimpleName()+" "); //獲得方法名 buffer.append(method.getName() + "("); //獲得方法的參數類型,可能有多個參數,所以返回值為一個數組 Class[] parameterTypes = method.getParameterTypes(); //遍歷方法的參數類型 for (Class parameterType : parameterTypes) { //遍歷獲得每一個參數類型 buffer.append(parameterType.getSimpleName()+", "); } buffer.append(") { }"+"\n"); } //獲得方法有什么用??通常用來調用方法 //1.先通過方法名來獲得一個方法 Method setNameFun = clazz.getMethod("setName", String.class); //可以直接調用,調用方法method---調用o對象的setNameFun方法,參數為“小笨蛋” setNameFun.invoke(o, "小笨蛋"); //構造方法 buffer.append("\n" + "-------------constructions-------------------"+"\n"); Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors) { buffer.append(Modifier.toString(constructor.getModifiers()) + " "); buffer.append(constructor.getName() + " ("); Class[] parametertypes = constructor.getParameterTypes(); for (Class parametertype : parametertypes) { buffer.append(parametertype.getSimpleName() +","); } buffer.append(") { }"+"\n"); } buffer.append("}"); System.out.println(buffer); } }