Commons JXPath - Modifying Object Graphs
JXPath 除了可以 XPath 语法访问 JavaBeans、DOM/JDOM,也可以对其属性赋值。
以下面的 JavaBeans 为例。
package com.huey.jxpath; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Book { private String title; private Author[] authors; private Publisher publisher; private String isbn; private double price; }
package com.huey.jxpath; import java.util.Date; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Author { private String firstName; private String lastName; private char gender; private Date birthday; }
package com.huey.jxpath; import java.util.Map; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Publisher { private String name; private String address; private Map<String, String> contacts; }
初始化:
Author[] authors; Publisher publisher; Book book; authors = new Author[] { new Author("Eric", "Freeman", 'F', new Date()), new Author("ElElisabeth", "Freeman", 'M', new Date()) }; Map<String, String> contacts = new HashMap<String, String>(); contacts.put("tel", "010-12345678"); contacts.put("fax", "010-87654321"); contacts.put("email", "test@163.com"); publisher = new Publisher("中国电力出版社", "北京市XX区YY路Z号", contacts); book = new Book("Head First Design Patterns", authors, publisher, "9787508353937", 98.0);
Setting Properties
JXPathContext context = JXPathContext.newContext(book); context.setValue("publisher/name", "人民邮电出版社");
context.setValue("publisher/contacts/attribute::email", "test@gmail.com");
Creating Objects
当对 JavaBean 的复杂数据类型属性设置值时,如果属性没有实例化,则会抛出一个 JXPathException 异常。实现 AbstractFactory 接口后,再调用 context.createPath 方法,能够在复杂数据类型对象为 null 时,为其实例化。context.createPathAndSetValue 方法能够在实例化对象的同时设置值。
package com.huey.jxpath; import java.lang.reflect.Array; import java.util.HashMap; import org.apache.commons.jxpath.AbstractFactory; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.Pointer; public class BookFactory extends AbstractFactory { @Override public boolean createObject(JXPathContext context, Pointer pointer, Object parent, String name, int index) { if (parent instanceof Book && "authors".equals(name)) { Book book = (Book) parent; if (book.getAuthors() == null) { book.setAuthors(new Author[]{}); } int newSize = index + 1; int oldSize = book.getAuthors().length; if (newSize > oldSize) { Author[] newAuthors = (Author[]) resizeArray(book.getAuthors(), newSize); book.setAuthors(newAuthors); } return true; } if (parent instanceof Book && "publisher".equals(name)) { ((Book)parent).setPublisher(new Publisher()); return true; } if (parent instanceof Publisher && "contacts".equals(name)) { ((Publisher)parent).setContacts(new HashMap<String, String>()); return true; } return false; } /** * 调整数组长度,当新的数组长度大于旧的数组长度时,实例化所有新增的元素 * @param oldArray * @param newSize * @return */ private Object resizeArray (Object oldArray, int newSize) { int oldSize = Array.getLength(oldArray); Class<?> elementType = oldArray.getClass().getComponentType(); Object newArray = Array.newInstance(elementType, newSize); int preserveLength = Math.min(oldSize, newSize); if (preserveLength > 0) { System.arraycopy(oldArray, 0, newArray, 0, preserveLength); } try { for (int i = preserveLength; i < newSize; i++) { Array.set(newArray, i, elementType.newInstance()); } } catch (Exception e) { e.printStackTrace(); } return newArray; } }
JXPathContext context = JXPathContext.newContext(new Book()); context.setFactory(new BookFactory()); context.createPathAndSetValue("title", "hello jxpath"); context.createPathAndSetValue("authors[1]/gender", 'F'); // Map 对象的 xpath 必须使用 contacts/email 或 contacts/child::email 而不能使用 contacts[@email] 或 contacts/attribute::email context.createPathAndSetValue("publisher/contacts/child::email", "test@gmial.com");