java---解析XML文件,通过反射动态将XML内容封装到一个类中
本博客讲的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); } }