xpath解析xml
public class XPathParser {
private final Document document;
//是否開啓驗證
private boolean validation;
//加載本地DTD
private EntityResolver entityResolver;
//Properties標籤定義值
private Properties variables;
private XPath xpath;
}
解析DTD
spring專欄有自定義命名空間解析,就不闡述了
public class XMLMapperEntityResolver implements EntityResolver {
//指定mybatis-config.xml的DTD
private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
//指定dtd位置
private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
try {
if (systemId != null) {
String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
//查找sysId指定的DTD文檔,並讀取
if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
} else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
}
}
return null;
} catch (Exception e) {
throw new SAXException(e.toString());
}
}
}
構造Xpath和document
public XPathParser(String xml) {
//構造時,初始化xpath
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
//設置DTD解析器
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
// NOP
}
});
//加載XML
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
解析String
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
//設置節點默認值
result = PropertyParser.parse(result, variables);
return result;
}
public static String parse(String string, Properties variables) {
VariableTokenHandler handler = new VariableTokenHandler(variables);
//指定處理的佔位符格式爲${}
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
public class GenericTokenParser {
//佔位符開始標記
private final String openToken;
//佔位符結束標記
private final String closeToken;
//解析佔位符
private final TokenHandler handler;
/**
* 將text根據前後佔位符解析
* @param text
* @return
*/
public String parse(String text) {
if (text == null || text.isEmpty()) {
return "";
}
// search open token
//查找第一個開啓佔位符位置
int start = text.indexOf(openToken);
if (start == -1) {
return text;
}
char[] src = text.toCharArray();
int offset = 0;
//線程安全,記錄佔位符的值
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
if (start > 0 && src[start - 1] == '\\') {
// this open token is escaped. remove the backslash and continue.
//遇到轉義的開始標記,直接將前面的字符串以及開始標記追加
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
// found open token. let's search close token.
//查找到開始標記,且未轉義
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
//前面字符追加
builder.append(src, offset, start - offset);
offset = start + openToken.length();
//從offset後繼續查找結束標記
int end = text.indexOf(closeToken, offset);
while (end > -1) {
if (end > offset && src[end - 1] == '\\') {
// this close token is escaped. remove the backslash and continue.
//處理轉義的結束標記
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {
//將開始標記和結束標記之間字符添加
expression.append(src, offset, end - offset);
break;
}
}
if (end == -1) {
//未找到結束標記
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//將佔位符的值交給handler處理,並保存builder
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
//移動start
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}
private static class VariableTokenHandler implements TokenHandler {
//Properties標籤定義值,用於替換佔位符
private final Properties variables;
//是否支持佔位符中使用默認的功能
private final boolean enableDefaultValue;
//指定佔位符和默認值之間的分隔符
private final String defaultValueSeparator;
@Override
public String handleToken(String content) {
//variables非空
if (variables != null) {
String key = content;
//是否支持佔位符中使用默認值功能
if (enableDefaultValue) {
//查找分割符
final int separatorIndex = content.indexOf(defaultValueSeparator);
String defaultValue = null;
if (separatorIndex >= 0) {
//獲取佔位符名稱
key = content.substring(0, separatorIndex);
//獲取默認值
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
}
if (defaultValue != null) {
//查找指定佔位符
return variables.getProperty(key, defaultValue);
}
}
//不支持默認值,則直接找
if (variables.containsKey(key)) {
return variables.getProperty(key);
}
}
//variables空
return "${" + content + "}";
}
}
解析XNode
public List<XNode> evalNodes(Object root, String expression) {
//XNode,mybatis實現,封裝node
List<XNode> xnodes = new ArrayList<>();
NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
xnodes.add(new XNode(this, nodes.item(i), variables));
}
return xnodes;
}
public class XNode {
//w3c對象
private final Node node;
//節點名
private final String name;
//節點內容
private final String body;
//節點屬性集合
private final Properties attributes;
//Properties對應配置文件
private final Properties variables;
private final XPathParser xpathParser;
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
this.attributes = parseAttributes(node);
this.body = parseBody(node);
}
private Properties parseAttributes(Node n) {
Properties attributes = new Properties();
//獲取節點的屬性集合
NamedNodeMap attributeNodes = n.getAttributes();
if (attributeNodes != null) {
for (int i = 0; i < attributeNodes.getLength(); i++) {
Node attribute = attributeNodes.item(i);
//使用PropertyParser 處理每個屬性中的佔位符
String value = PropertyParser.parse(attribute.getNodeValue(), variables);
attributes.put(attribute.getNodeName(), value);
}
}
return attributes;
}
private String parseBody(Node node) {
String data = getBodyData(node);
//當前節點不是文本節點
if (data == null) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
data = getBodyData(child);
if (data != null) {
break;
}
}
}
return data;
}
private String getBodyData(Node child) {
//只處理文本內容
if (child.getNodeType() == Node.CDATA_SECTION_NODE
|| child.getNodeType() == Node.TEXT_NODE) {
String data = ((CharacterData) child).getData();
//使用PropertyParser 處理文本節點中的佔位符
data = PropertyParser.parse(data, variables);
return data;
}
return null;
}
}
對反射支持
public class Reflector {
//對應的Class 類型
private final Class<?> type;
//可讀屬性的名稱集合,可讀屬性就是存在相應getter 方法的屬性,初始值爲空數紐
private final String[] readablePropertyNames;
//可寫屬性的名稱集合,可寫屬性就是存在相應setter 方法的屬性,初始值爲空數紐
private final String[] writablePropertyNames;
//記錄了屬性相應的setter 方法, key 是屬性名稱, value 是Invoker 對象
private final Map<String, Invoker> setMethods = new HashMap<>();
//屬性相應的getter 方法集合, key 是屬性名稱, value 也是Inv o ker 對象
private final Map<String, Invoker> getMethods = new HashMap<>();
//記錄了屬性相應的setter 方法的參數值類型, ke y 是屬性名稱, value 是setter 方法的參數類型
private final Map<String, Class<?>> setTypes = new HashMap<>();
//記錄了屬性相應的getter 方法的返回位類型, key 是屬性名稱, value 是getter 方法的返回位類型
private final Map<String, Class<?>> getTypes = new HashMap<>();
//記錄了默認構造方法
private Constructor<?> defaultConstructor;
//記錄了所有屬性名稱的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
public Reflector(Class<?> clazz) {
type = clazz;
//查找clazz 的默認構造方法(無參構造方法)
addDefaultConstructor(clazz);
//處理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合
addGetMethods(clazz);
//處理clazz 中的set ter 方法,填充setMethods 集合和set Types 集合
addSetMethods(clazz);
//處理沒有g etter I setter 方法的字段
addFields(clazz);
//根據getMethodslsetMethods 集合,初始化可讀/寫屬性的名稱集合
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
//初始化caseinsensitivePropertyMap 集合,其中記錄了所有大寫格式的屬性名稱
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//獲取指定類以及其父類和接口中定義的方法
Method[] methods = getClassMethods(clazz);
//對get方法過濾
Arrays.stream(methods).filter(m ->
//JavaBean 中getter 方法的方法名長度大於3 且必須以” get ” 開頭
m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m ->
//記錄到conflictingGetters集合中
addMethodConflict(conflictingGetters,
//獲取對應的屬性名稱
PropertyNamer.methodToProperty(m.getName()), m));
//對方法同名返回值不一樣的處理
resolveGetterConflicts(conflictingGetters);
}
private Method[] getClassMethods(Class<?> clazz) {
//用於記錄指定類中定義的全部方法的唯一簽名以及對應的Meth od 對象
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
//記錄currentClass 這個類中定義的全部方法.橋方法除外
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
// we also need to look for interface methods -
// because the class may be abstract
//記錄接口中定義的方法
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
//獲取父類,繼續while 循環
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[0]);
}
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
//過濾橋接方法,也就是爲了兼容泛型,重新定義了一個方法,調用原來的方法,定位橋
if (!currentMethod.isBridge()) {
//方法得到的方法簽名是全局唯一的,可以作爲該方法 的唯一標識
String signature = getSignature(currentMethod);
// check to see if the method is already known
// if it is known, then an extended class must have
// overridden a method
//檢測是否在子類中已經添加過該方法,如果在子類中已經添加過,無須再向uniqueMethods 集合中添加該方法了
if (!uniqueMethods.containsKey(signature)) {
//記錄該簽名和方法的對應關係
uniqueMethods.put(signature, currentMethod);
}
}
}
}
}
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
String propName = entry.getKey();
boolean isAmbiguous = false;
for (Method candidate : entry.getValue()) {
if (winner == null) {
winner = candidate;
continue;
}
//同一屬性名稱存在多個getter方法,則需要比較這些getter方法的返回,選擇getter方法
//迭代過程中的臨時變量,用於記錄迭代到目前爲止,最適合作爲getter 方法的Method
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
//對boolean處理
if (!boolean.class.equals(candidateType)) {
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
//當前最適合的方法的返回佳是當前方法返回的子類,什麼都不做,當前最適合的方法 依然不變
} else if (winnerType.isAssignableFrom(candidateType)) {
//當前方法的返回位是當前最適合的方法的返回值的子類,更新臨時交量getter,當前的getter 方法成爲最適合的getter 方法
winner = candidate;
} else {
//返回值相同,二義性,拋出異常
isAmbiguous = true;
break;
}
}
//添加到getMethods 集合並填充get Types 集合
addGetMethod(propName, winner, isAmbiguous);
}
}