xml
本文主要是關於使用commons-betwixt組件實現xml資訊到Java Bean的轉化的一些感受,實現這一功能的核心類是BeanReader,如果你看過betwixt的原始碼,你會發現
public class BeanReader extends Digester{}
如果你細看一下BeanReader的原始碼,betwixt在處理xml-->JavaBean轉化時其實是調用Digester這個類的parse方法。但除了此方式可以實現轉化的功能以外,BeanReader自身也有一些新的機制對xml進行轉化,完善了Digester的很多欠缺功能,讓轉化更加方便。
首先,來看一下BeanReader完全使用Digester的parse方法進行轉化的流程。(其實覺得說是Digester怎樣處理轉化更為恰當)
1。xml資訊
<address-book>
<person id="1" category="acquaintance" try="would be ignored">
<name>Gonzo</name>
<email type="business">gonzo@muppets.com</email>
<gender result="the whole tag would be ignored">male</gender>
</person>
<person id="2" category="rolemodel">
<name>Kermit</name>
<email type="business">kermit@muppets.com</email>
<email type="home">kermie@acme.com</email>
</person>
</address-book>
2。定義實體物件Person和AddressBook
import java.util.HashMap;
import java.util.Iterator;
public class Person {
private int id;
private String category;
private String name;
private HashMap emails = new HashMap();
/**
* A unique id for this person. Note that the Digester automatically
* converts the id to an integer.
*/
public void setId(int id) {
this.id = id;
}
public void setCategory(String category) {
this.category = category;
}
public void setName(String name) {
this.name = name;
}
/** we assume only one email of each type... */
public void addEmail(String type, String address) {
emails.put(type, address);
}
public void print() {
System.out.println("Person #" + id);
System.out.println(" category=" + category);
System.out.println(" name=" + name);
for(Iterator i = emails.keySet().iterator(); i.hasNext(); ) {
String type = (String) i.next();
String address = (String) emails.get(type);
System.out.println(" email (type " + type + ") : " + address);
}
}
import java.util.Iterator;
import java.util.LinkedList;
public class AddressBook {
LinkedList person = new LinkedList();
public LinkedList getPerson(){
return person;
}
public void addPerson(Person p) {
person.addLast(p);
}
public void print() {
System.out.println("Address book has " + person.size() + " entries");
for(Iterator i = person.iterator(); i.hasNext(); ) {
Person p = (Person) i.next();
p.print();
}
}
3。實現轉化功能
public class AddressBookDigester{
public static void main(String[] args) throws IOException, SAXException, IntrospectionException{
// 建立一個BeanReader執行個體
BeanReader reader = new BeanReader();
// 建立AddressBook執行個體,並將其壓入棧頂。
AddressBook book = new AddressBook();
reader.push(book);
// 增加規則
addRules(reader);
reader.parse(new File("examples.xml"));
book.print();
}
private static void addRules(BeanReader d) {
// 當遇到<person>時,建立類Person的一個執行個體,並將其壓入棧頂
d.addObjectCreate("address-book/person", Person.class);
// 將<person>標籤的屬性(attribute)與棧頂Person類對象的屬性(property)設定方法根據各自的名字進行映射,(例如,將
標籤屬性id與屬性設定方法setId進行映射,將標籤屬性category與屬性設定方法setCategory進行映射),然後將屬性的值作參數傳遞給執行相
應的方法。
// 如果某標籤屬性沒法通過名字找到相應的屬性設定方法,則此標籤屬性被忽略(如example.xml中第一個<person>的try屬
性)。
d.addSetProperties("address-book/person");
// 調用第二棧頂對象(AddressBook執行個體)的addPerson方法,以棧對象(Person執行個體)的對象為參數
d.addSetNext("address-book/person", "addPerson");
// 當遇到<person>的子項目<name>時,調用棧頂對象(Person執行個體)的setName方法。
// 此處addCallMethod方法的第一參數是規則,第二個參數是方法的名字,第三個是參數的數量(為0時,表示只有一個參數
,且參數的值是元素的內容)
d.addCallMethod("address-book/person/name", "setName", 0);
// 當遇到<person>的子項目<email>時,調用棧頂對象(Person執行個體)的addEmail
// 方法,addEmail方法有兩個參數,取值分別來自<email>的屬性type的值和<email>本身的內容。
// 此處addCallParam方法的第一參數是規則,第二個參數是指明被呼叫者法(addEmail)參數的序號,第三個是參數為字串
時指屬性的名字)
d.addCallMethod("address-book/person/email", "addEmail", 2);
d.addCallParam("address-book/person/email", 0, "type");
d.addCallParam("address-book/person/email", 1);
}
}
運行結果如下:
Address book has 2 entries
Person #1
category=acquaintance
name=Gonzo
email (type business) : gonzo@muppets.com
Person #2
category=rolemodel
name=Kermit
email (type business) : kermit@muppets.com
email (type home) : kermie@acme.com
其次,再看看不使用Digester的add***Method()方法,對轉化屬性進行配置的方式。
1。定義實體類
public class PersonBean {
private String name;
private int age;
/** Need to allow bean to be created via reflection */
public PersonBean() {}
public PersonBean(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "PersonBean[name='" + name + "',age='" + age + "']";
}
}
2.person.xml檔案
<beans>
<personbean>
<age>22</age>
<name>luna</name>
</personbean>
</beans>
3。實現轉化功能
import java.io.StringReader;
import org.apache.commons.betwixt.io.BeanReader;
public class ReadApp {
public static final void main(String args[]) throws Exception{
// 初始化BeanReader並對其進行配置
BeanReader reader = new BeanReader();
reader.getXMLIntrospector().getConfiguration()
.setAttributesForPrimitives(false);
reader.getBindingConfiguration().setMapIDs(false);
// 註冊bean,告訴betwixt把xml轉化成什麼bean
reader.registerBeanClass("personbean", PersonBean.class);
// 得到一個Object對象並列印
PersonBean person = (PersonBean)reader.parse(new File("person.xml"));
System.out.println(person);
}
}
運行結果:
PersonBean[name='luna',age='22']
說明幾點:
Digester類的parse()是xml向javabean轉化的核心方法,兩者的最大區別是,前者通過詳細的add***()方法,告訴了BeanReader的轉化步驟,而後者則是用registerBeanClass()註冊將要轉化成的bean物件類型,而其中的方法調用則是BeanReader自動去按號入座完成的。而後者也一次只能轉化一個bean對象,可採用迭代的方法對多哥對象進行操作。
前者適用於在已經知道bean類型及具體屬性的情況下,可以用add***()一步步的設定轉化的步驟,而如果產生的對象是預先未知的,只有在知道結果的情況下才知道它的屬性,這個時候只要在後者方法中使用registerBeanClass()把該類註冊給betwxit,剩下的功能,這個組件就可以自動完成了 。