获取XML配置数据
1 详细功能
1.1 详细功能
为了实现theme的即配即用,规定theme的配置使用xml文件,框架的配置和模块的配置的provider都可以由用户指定,可以在运行时传入框架配置的provider,然后在 框架配置里面指定了模块配置的provider。
因此本章讲述获取默认的xml方式配置的数据。
1.2 功能边界
只是负责配置文件为xml时候的读取、解析并获取xml中的配置数据。其实就是实现前面桥接模式的抽象部分的接口。
2 内部实现
2.1 加入解释器模式
2.1.1面临的问题
自己解析xml,本来也没有什么特别困难的,但是问题就在于,如果xml文件的格式发生了变化,那么读取配置文件的程序就需要做出相应的变更,严重的时候,几乎相当于完全重写程序。
那么怎么解决当xml的结构发生改变过后,能够很方便的获取相应元素、或者是属性的值,而不用再去修改解析xml的程序呢?
2.1.2用解释器模式来解决
1)模式定义:
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
2)模式本质:
解释器模式的本质:分离实现,解释执行。
3)基础知识:
(1)解释器模式使用解释器对象来表示和处理相应的语法规则,一般一个解释器处理一条语法规则。
(2)解释器模式没有定义谁来构建抽象语法树,把这个工作交给了客户端处理
(3)使用解释器模式的时候,应该先定义好相应的语法规则,并根据规则制定解释器的语法树;然后客户端在调用解释器进行解释操作的时候,需要自行构建符合语法规则要求的语法树;而解释器模式只是负责解释并执行。
(4)从实质上看,解释器模式的思路仍然是分离、封装、简化,跟很多模式是一样的。
4)常见应用场景:
语法较为简单的自定义表达式解析,比如自定义xml文档的解析,文档结构不要太复杂,那不适合使用解释器模式。
解释器模式的基本实现,沿用了《研磨设计模式》一书的解释器模式一章中最后实现的通用解析程序,由于篇幅关系,这里就不会那么详细的讲述实现的细节,这里更关心实现的思路,比较高层一点。因此更多的细节请查看源代码或是《研磨设计模式》一书的解释器模式一章的内容。
很明显,使用解释器模式来解决这个问题是一个很好的思路。
设计一个简单的表达式规则,然后通过解释器模式来进行解析执行,从而获取到xml中的值。
约定简单的语法规则如下:
1:为表达式设计简单的文法
为了通用,用root表示根元素,a、b、c、d等来代表元素,一个简单的xml如下:
Ø获取单个元素的值:从根元素开始,一直到想要获取值的元素,元素中间用“/”分隔,根元素前不加“/”。比如表达式“root/a/b/c”就表示获取根元素下、a元素下、b元素下的c元素的值
Ø获取单个元素的属性的值:要获取值的属性一定是表达式的最后一个元素的属性,在最后一个元素后面添加“.”然后再加上属性的名称。比如表达式“root/a/b/c.name”就表示获取根元素下、a元素下、b元素下、c元素的name属性的值
Ø获取相同元素名称的值,当然是多个:要获取值的元素一定是表达式的最后一个元素,在最后一个元素后面添加“ ” 。 比 如 表 达 式 “ r o o t / a / b / d ”。比如表达式“root/a/b/d ”。比如表达式“root/a/b/d”就表示获取根元素下、a元素下、b元素下的多个d元素的值的集合
Ø获取相同元素名称的属性的值,当然也是多个:要获取属性值的元素一定是表达式的最后一个元素,在最后一个元素后面添加“ ” , 然 后 在 后 面 添 加 “ . ” 然 后 再 加 上 属 性 的 名 称 , 在 属 性 名 称 后 面 也 添 加 “ ”,然后在后面添加“.”然后再加上属性的名称,在属性名称后面也添加“ ”,然后在后面添加“.”然后再加上属性的名称,在属性名称后面也添加“”。比如表达式“root/a/b/d . i d .id .id”就表示获取根元素下、a元素下、b元素下的多个d元素的id属性的值的集合
Ø如果要获取某个需要区分的元素下面的值,那么对于判断使用这样的语法:[属性名=值],属性名和值都不需要加引号,比如:要想获取id=”e1”的e元素下面的f元素的值,就是用这样的表达式:root/a/e$[id=e1]/f。
2:核心代码示例
(1)先来看看ReadXmlExpression的示例,示例代码如下:
/**
- 用于处理自定义Xml取值表达式的接口
/
public abstractclassReadXmlExpression{
/*- 解释表达式
- @param c上下文
- @return 解析过后的值,为了通用,可能是单个值,也可能是多个值,
- 因此就返回一个数组
*/
publicabstract String[] interpret(Context c);
}
(2)看看以单个元素作为中间元素的实现,要注意跟以前的实现相比,添加了对条件的判断,示例代码如下:
/**
-
单个元素作为非终结符的解释器
/
public class ElementExpression extends ReadXmlExpression{
/*- 用来记录组合的ReadXmlExpression元素
/
private List eles = new ArrayList();
/* - 元素的名称
/
private String eleName = “”;
/* - 表达式中设置的条件表达式
*/
private String condition = “”;
public ElementExpression(String eleName,String condition){
this.eleName = eleName;
this.condition = condition;
}
public boolean addEle(ReadXmlExpression ele){
this.eles.add(ele);
return true;
}
public boolean removeEle(ReadXmlExpression ele){
this.eles.remove(ele);
return true;
}
public boolean removeAllEle(){
this.eles.clear();
return true;
}
public String[] interpret(Context c) {
//先取出上下文里的父级元素
List pEles = c.getPreEles();
Element ele = null;
//把当前获取的元素放到上下文里面,这次是获取多个元素
List nowEles = new ArrayList();
if(pEles.size()==0){
//说明现在获取的是根元素
ele = c.getDocument().getDocumentElement();
pEles.add(ele);
c.setPreEles(pEles);
}else{
for(Element tempEle : pEles){
nowEles.addAll(c.getNowEles(tempEle, eleName));
if(nowEles.size()>0){
//找到一个就停止
break;
}
}List tempList = new ArrayList();
//判断是否满足条件,满足条件才放入
if(nowEles.size()>0 && c.judgeCondition(nowEles.get(0), condition)){
tempList.add(nowEles.get(0));
c.setPreEles(tempList);
}
}//循环调用子元素的interpret方法 String [] ss = null; for(ReadXmlExpression tempEle : eles){ ss = tempEle.interpret(c); } return ss;
}
public void setEles(List eles) {
this.eles = eles;
}
public List getEles() {
return eles;
}
} - 用来记录组合的ReadXmlExpression元素
(3)看看以多个元素作为中间元素的实现,要注意跟以前的实现相比,添加了对条件的判断,示例代码如下:
/**
-
多个元素做为非终结符的解释处理对象
/
public class ElementsExpression extends ReadXmlExpression{
/*-
用来记录组合的ReadXmlExpression元素
/
privateList eles = new ArrayList();
/* -
元素名字
*/
private String eleName = “”;
private String condition;
publicElementsExpression(String eleName,String condition){
this.eleName = eleName;
this.condition = condition;
}
public booleanremoveAllEle(){
this.eles.clear();
return true;
}
public String[] interpret(Context c) {
//先取出上下文里的父级元素
List pEles = c.getPreEles();
//把当前获取的元素放到上下文里面,这次是获取多个元素
List nowEles = new ArrayList();for(Element ele : pEles){
nowEles.addAll(c.getNowEles(ele,eleName));
}//循环判断是否满足条件,满足条件才放入
Iterator it = nowEles.iterator();
while(it.hasNext()){
Element ele = (Element)it.next();
if(!c.judgeCondition(ele, condition)){
it.remove();
}
}
c.setPreEles(nowEles);//循环调用子元素的interpret方法
String [] ss = null;
for(ReadXmlExpression ele : eles){
ss = ele.interpret©;
}
return ss;
}
public booleanaddEle(ReadXmlExpression ele){
this.eles.add(ele);
return true;
}
public booleanremoveEle(ReadXmlExpression ele){
this.eles.remove(ele);
return true;
}
public voidsetEles(List eles) {
this.eles = eles;
}
publicList getEles() {
return eles;
}
} -
(4)看看以单个元素作为终结符元素的实现,要注意跟以前的实现相比,添加了对条件的判断,示例代码如下:
/**
-
元素作为终结符对应的解释器
/
public classElementTerminalExpression extendsReadXmlExpression{
/*- 元素的名字
*/
private String eleName = “”;
private String condition;
publicElementTerminalExpression(String name,String condition){
this.eleName = name;
this.condition = condition;
}
public String[] interpret(Context c) {
//先取出上下文里的当前元素作为父级元素
List pEles = c.getPreEles();
//查找到当前元素名称所对应的xml元素
Element ele = null;
if(pEles.size() == 0){
//说明现在获取的是根元素
ele = c.getDocument().getDocumentElement();
}else{
//获取当前的元素
ele = c.getNowEles(pEles.get(0),eleName).get(0);
}//判断是否满足条件
if(!c.judgeCondition(ele,condition)){
return new String[0];
}//然后需要去获取这个元素的值
String[] ss = new String[1];
if(ele.getFirstChild()!=null){
ss[0] = ele.getFirstChild().getNodeValue();
}else{
ss[0] = “”;
}
return ss;
}
} - 元素的名字
(5)看看以多个元素作为终结符元素的实现,要注意跟以前的实现相比,添加了对条件的判断,示例代码如下:
/**
- 以多个元素作为终结符的解释处理对象
/
public class ElementsTerminalExpression extends ReadXmlExpression{
/*-
元素的名称
*/
private String eleName = “”;
private String condition;
public ElementsTerminalExpression(String name,String condition){
this.eleName = name;
this.condition = condition;
}
public String[] interpret(Context c) {
//先取出上下文里的父级元素
List pEles = c.getPreEles();
//获取当前的多个元素
List nowEles = newArrayList();for(Element ele : pEles){
nowEles.addAll(c.getNowEles(ele,eleName));
}
//循环判断是否满足条件,满足条件才放入
Iterator it = nowEles.iterator();
while(it.hasNext()){
Element ele = (Element)it.next();
if(!c.judgeCondition(ele,condition)){
it.remove();
}
}
//然后需要去获取这些元素的值
String[] ss = newString[nowEles.size()];
for(int i=0;i<ss.length;i++){
if(nowEles.get(i).getFirstChild()!=null){
ss[i] = nowEles.get(i).getFirstChild().getNodeValue();
}else{
ss[i] = “”;
}
}
return ss;
}
}
-
(6)看看以单个属性作为终结符元素的实现,示例代码如下:
/**
-
属性作为终结符对应的解释器
/
public class PropertyTerminalExpression extendsReadXmlExpression{
/*- 属性的名字
/
private String propName;
/* - 保留condition是为了今后扩展需要
*/
public PropertyTerminalExpression(String propName,String condition){
this.propName = propName;
}
public String[] interpret(Context c) {
//直接获取最后的元素的属性的值
String[] ss = new String[1];
ss[0] = c.getPreEles().get(0).getAttribute(this.propName);
return ss;
}
} - 属性的名字
(7)看看以多个属性作为终结符元素的实现,示例代码如下:
/**
-
以多个元素的属性做为终结符的解释处理对象
/
public class PropertysTerminalExpression extends ReadXmlExpression{
/*- 属性名字
*/
private String propName;
// private String condition;
publicPropertysTerminalExpression(String propName,String condition){
this.propName = propName;
// this.condition = condition;
}
public String[] interpret(Context c) {
//获取最后的多个元素
List eles = c.getPreEles();String[] ss = newString[eles.size()];
//循环多个元素,获取每个的属性的值
for(int i=0;i<ss.length;i++){
ss[i] = eles.get(i).getAttribute(this.propName);
}
return ss;
}
} - 属性名字
(8)该来看看上下文的实现了,注意跟以前的实现相比,上下文里面真正实现了条件的判断,作为公共的功能提供给各个解释器使用,示例代码如下:
/**
-
上下文,用来包含解释器需要的一些全局信息
/
public class Context {
/*-
Dom解析Xml的Document对象
/
private Document document = null;
/* -
上一次被处理的多个元素
/
private List preEles = new ArrayList();
/* -
构造方法
-
@param filePathName 需要读取的xml的路径和名字
-
@throws Exception
/
public Context(String filePathName) throws Exception{
//通过辅助的Xml工具类来获取被解析的xml对应的Document对象
this.document = XmlUtil.getRoot(filePathName);
}
/* -
各个Expression公共使用的方法,
-
根据父元素和当前元素的名称来获取当前的多个元素的集合
-
@param pEle 父元素
-
@param eleName 当前元素的名称
-
@return 当前的多个元素的集合
*/
public List getNowEles(Element pEle,String eleName){
List elements = newArrayList();
NodeList tempNodeList = pEle.getChildNodes();
for(inti=0;i<tempNodeList.getLength();i++){
if(tempNodeList.item(i)instanceof Element){
Element nowEle = (Element)tempNodeList.item(i);
if(nowEle.getTagName().equals(eleName)){
elements.add(nowEle);
}
}
}
return elements;
}
public booleanjudgeCondition(Element ele,String condition){
//目前只是支持判断元素的属性等于某个值
if(condition==null || condition.trim().length()==0){
return true;
}String ss[] = condition.split("=");
if(ss[1]!=null && ss[1].equals(ele.getAttribute(ss[0]))){
return true;
}
return false;
}
public void init(){
preEles = newArrayList();
}public Document getDocument() {
return document;
}
public List getPreEles() {
return preEles;
}
public voidsetPreEles(List nowEles) {
this.preEles = nowEles;
}
}
本文由微信公众号——架构设计一起学 推出,
可点击“阅读原文”了解更多! -