3. 高级功能

3.1. 配置GroupTemplate

Beetl建议通过配置文件配置GroupTemplate,主要考虑到IDE插件未来可能会支持Beetl模板,模板的属性,和函数等如果能通过配置文件获取,将有助于IDE插件识别。 配置GroupTemplate有俩种方法

配置文件: 默认配置在/org/beetl/core/beetl-default.properties 里,Beetl首先加载此配置文件,然后再加载classpath里的beetl.properties,并用后者覆盖前者。配置文件通过Configuration类加载,因此加载完成后,也可以通过此类API来修改配置信息

通过调用GroupTemplate提供的方法来注册函数,格式化函数,标签函数等

配置文件分为三部分,第一部分是基本配置,在第一节讲到过。第二部分是资源类配置,可以在指定资源加载类,以及资源加载器的属性,如下

RESOURCE_LOADER=org.beetl.core.resource.ClasspathResourceLoader

#资源配置,resource后的属性只限于特定ResourceLoader

#classpath 根路径

RESOURCE.root= /

#是否检测文件变化

RESOURCE.autouCheck= true

第1行指定了模板加载器类,在beetl与其他框架集成的时候,模板加载器不一定根据这个配置,比如spring,它的RESOURCE_LOADER以spring的配置为准

第4行指定了模板根目录的路径,此处/ 表示位于classpath 根路径下

第6行是否自动检测模板变化,默认为true,开发环境下自动检测模板是否更改。关于如何自定义ResouceLoader,请参考下一章

配置文件第三部分是扩展部分,如方法,格式化函数等

##### 扩展 ##############

## 内置的方法

FN.date = org.beetl.ext.fn.DateFunction

FN.nvl = org.beetl.ext.fn.NVLFunction

.................

##内置的功能包

FNP.strutil = org.beetl.ext.fn.StringUtil

##内置的格式化函数

FT.dateFormat = org.beetl.ext.format.DateFormat

FT.numberFormat = org.beetl.ext.format.NumberFormat

.................

##内置的默认格式化函数

FTC.java.util.Date = org.beetl.ext.format.DateFormat

FTC.java.sql.Date = org.beetl.ext.format.DateFormat

## 标签类

TAG.include= org.beetl.ext.tag.IncludeTag

TAG.includeFileTemplate= org.beetl.ext.tag.IncludeTag

TAG.layout= org.beetl.ext.tag.LayoutTag

TAG.htmltag= org.beetl.ext.tag.HTMLTagSupportWrapper

FN前缀表示Function,FNP前缀表示FunctionPackage,FT表示format函数,FTC表示类的默认Format函数,TAG表示标签类。Beetl强烈建议通过配置文件加载扩展。以便随后IDE插件能识别这些注册函数

3.2. 自定义方法

3.2.1. 实现Function

public class Print implements Function{

public String call(Object[] paras, Context ctx){

Object o = paras[0];

if (o != null){

try{

ctx.byteWriter.write(o.toString());

}catch (IOException e){

throw new RuntimeException(e);

}

}

return "";

}

}

call方法有俩个参数,第一个是数组,这是由模板传入的,对应着模板的参数,第二个是Context,包含了模板的上下文,主要提供了如下属性

byteWriter 输出流

template 模板本身

gt GroupTemplate

globalVar 该模板对应的全局变量

byteOutputMode 模板的输出模式,是字节还是字符

safeOutput 模板当前是否处于安全输出模式

其他属性建议不熟悉的开发人员不要乱动

call方法要求返回一个Object,如果无返回,返回null即可

为了便于类型判断,call方法最好返回一个具体的类,如date函数返回的就是java.util.Date

call方法里的任何异常应该抛出成Runtime异常

3.2.2. 使用普通的java类

尽管实现Function对于模板引擎来说,是效率最高的方式,但考虑到很多系统只有util类,这些类里的方法仍然可以注册为模板函数。其规则很简单,就是该类的所有public方法。如果还需要Context 变量,则需要在方法最后一个参数加上Context即可,如

public class util{

public String print(Object a, Context ctx){

//balabala...

}

}

注意

从beetl效率角度来讲,采用普通类效率不如实现Function调用

采用的普通java类尽量少同名方法。这样效率更低。beetl调用到第一个适合的同名方法。而不像java那样找到最匹配的

方法名支持可变数组作为参数

方法名最后一个参数如果是Context,则beetl会传入这个参数。

3.2.3. 使用模板文件作为方法

可以不用写java代码,模板文件也能作为一个方法。默认情况下,需要将模板文件放到Root的functions目录下,且扩展名为.html(可以配置文件属性来修改这俩个默认值) 方法参数分别是para0,para1…..

如下root/functions/page.fn

//para0,para1 由函数调用传入

var current = para0,total = para1,style=para2!'simple'

%>

当前页面 ${current},总共${total}

则在模板中

page(current,total);

%>

允许使用return 表达式返回一个变量给调用者,如模板文件functions\now.html

return date();

%>

在任何模板里都可以调用:

hello time is ${now(),'yyyy-MM-dd'}

也可以在functions建立子目录,这样function则具有namespace,其值就是文件夹名

3.3. 自定义格式化函数

需要实现Format接口

public class DateFormat implements Format{

public Object format(Object data, String pattern){

if (data == null)

return null;

if (Date.class.isAssignableFrom(data.getClass())){

SimpleDateFormat sdf = null;

if (pattern == null){

sdf = new SimpleDateFormat();

}else{

sdf = new SimpleDateFormat(pattern);

}

return sdf.format((Date) data);

}else{

throw new RuntimeException("Arg Error:Type should be Date");

}

}

}

data 参数表示需要格式化的对象,pattern表示格式化模式,开发时候需要考虑pattern为null的情况

也可以实现ContextFormat 类抽象方法,从而得到Context,获取外的格式化信息。

public abstract Object format(Object data,String pattern,Context ctx);

3.4. 自定义标签

标签形式有俩种,一种是标签函数,第二种是html tag。第二种实际上在语法解析的时候会转化成第一种,其实现是HTMLTagSupportWrapper,此类将会寻找root/htmltag目录下同名的标签文件作为模板来执行。类似普通模板一样,在此就不详细说了

3.4.1. 标签函数

标签函数类似jsp2.0的实现方式,需要实现Tag类的render方法即可

public class DeleteTag extends Tag{

@Override

public void render(){

// do nothing,just ignore body

ctx.byteWriter.write("被删除了,付费可以看")

}

}

如上一个最简单的Tag,将忽略tag体,并输出内容

public class XianDeDantengTag extends Tag{

@Override

public void render(){

doBodyRender();

}

}

此类将调用父类方法doBodyRender,渲染tag body体

public class CompressTag extends Tag{

@Override

public void render(){

BodyContent content = getBodyContent();

String content = content.getBody();

String zip = compress(conent);

ctx.byteWriter.write(zip);

}

}

此类将调用父类方法getBodyContent ,获得tag body后压缩输出

tag类提供了如下属性和方法供使用

args 传入标签的参数

gt GroupTemplate

ctx Context

bw 当前的输出流

bs 标签体对应的语法树,不熟悉勿动

3.5. 自定义虚拟属性

可以为特定类注册一个虚拟属性,也可以为一些类注册虚拟属性

public void registerVirtualAttributeClass(Class cls, VirtualClassAttribute virtual) 实现VirtualClassAttribute方法可以为特定类注册一个需要属性,如下代码:

gt.registerVirtualAttributeClass(User.class, new VirtualClassAttribute() {

@Override

public String eval(Object o, String attributeName, Context ctx){

User user = (User) o;

if(attributeName.equals("ageDescritpion")){

if (user.getAge() < 10){

return "young";

}else{

return "old";

}

}

}

});

User类的所有虚拟属性将执行eval方法,此方法根据年纪属性来输出对应的描述。

public void registerVirtualAttributeEval(VirtualAttributeEval e) 为一些类注册需要属性,VirtualAttributeEval.isSupport方法将判断是否应用虚拟属性到此类

如下是虚拟属性类的定义

public interface VirtualClassAttribute{

public Object eval(Object o, String attributeName, Context ctx);

}

public interface VirtualAttributeEval extends VirtualClassAttribute{

public boolean isSupport(Class c, String attributeName);

}

3.6. 使用额外的资源加载器

某些情况下,模板来源不止一处,GroupTemplate配置了一个默认的资源加载器,如果通过gt.getTemplate(key),将调用默认的ResourceLoader,获取模板内容,然后转化为beetl脚本放入到缓存里。你也可以传入额外的资源管理器加载模板,通过调用gt.getTemplate(key,otherLoader)来完成;

GroupTemplate gt = new GroupTemplate(conf,fileLoader)

//自定义,参考下一节

MapResourceLoader dbLoader = new MapResourceLoader(getData());

Template t = gt.getTemplate("db:1", dbLoader);

private Map getData(){

Map data = new HashMap();

data.put("db:1", "${a}");

return data;

}

对于更复杂的模板资源来源,也可以自定义一个资源加载来完成,参考下一节

3.7. 自定义资源加载器

如果模板资源来自其他地方,如数据库,或者混合了数据库和物理文件,或者模板是加密的,则需要自定义一个资源加载器。资源加载器需要实现ResourceLoader类。如下:

public interface ResourceLoader{

/**

* 根据key获取Resource

*

* @param key

* @return

*/

public Resource getResource(String key);

/** 检测模板是否更改,每次渲染模板前,都需要调用此方法,所以此方法不能占用太多时间,否则会影响渲染功能

* @param key

* @return

*/

public boolean isModified(Resource key);

/**

* 关闭ResouceLoader,通常是GroupTemplate关闭的时候也关闭对应的ResourceLoader

*/

public void close();

/** 一些初始化方法

* @param gt

*/

public void init(GroupTemplate gt);

/** 用于include,layout等根据相对路径计算资源实际的位置.

* @param resource 当前资源

* @param key

* @return

*/

public String getResourceId(Resource resource, String key);

}

如下是一个简单的内存ResourceLoader

public class MapResourceLoader implements ResourceLoader{

Map data;

public MapResourceLoader(Map data){

this.data = data;

}

@Override

public Resource getResource(String key){

String content = (String) data.get(key);

if (content == null)

return null;

return new StringTemplateResource(content, this);

}

@Override

public boolean isModified(Resource key){

return false;

}

@Override

public boolean exist(String key){

return data.contain(key);

}

@Override

public void close(){

}

@Override

public void init(GroupTemplate gt){

}

@Override

public String getResourceId(Resource resource, String id){

//不需要计算相对路径

return id;

}

}

init方法可以初始化GroupTemplate,比如读取配置文件的root属性,autoCheck属性,字符集属性,以及加载functions目录下的所有模板方法 如FileResourceLoader 的 init方法

@Override

public void init(GroupTemplate gt){

Map resourceMap = gt.getConf().getResourceMap();

if (this.root == null){

this.root = resourceMap.get("root");

}

if (this.charset == null){

this.charset = resourceMap.get("charset");

}

if (this.functionSuffix == null){

this.functionSuffix = resourceMap.get("functionSuffix");

}

this.autoCheck = Boolean.parseBoolean(resourceMap.get("autoCheck"));

File root = new File(this.root, this.functionRoot);

this.gt = gt;

if (root.exists()){

readFuntionFile(root, "", "/".concat(functionRoot).concat("/"));

}

}

readFuntionFile 方法将读取functions下的所有模板,并注册为方法

protected void readFuntionFile(File funtionRoot, String ns, String path){

String expected = ".".concat(this.functionSuffix);

File[] files = funtionRoot.listFiles();

for (File f : files){

if (f.isDirectory()){

//读取子目录

readFuntionFile(f, f.getName().concat("."), path.concat(f.getName()).concat("/"));

} else if (f.getName().endsWith(functionSuffix)){

String resourceId = path + f.getName();

String fileName = f.getName();

fileName = fileName.substring(0, (fileName.length() - functionSuffix.length() - 1));

String functionName = ns.concat(fileName);

FileFunctionWrapper fun = new FileFunctionWrapper(resourceId);

gt.registerFunction(functionName, fun);

}

}

}

Resource类需要实现OpenReader方法,以及isModified方法。对于模板内容存储在数据库中,openReader返回一个Clob,isModified 则需要根据改模板内容对应的lastUpdate(通常数据库应该这么设计)来判断模板是否更改

public abstract class Resource{

/**

* 打开一个新的Reader

*

* @return

*/

public abstract Reader openReader();

/**

* 检测资源是否改变

*

* @return

*/

public abstract boolean isModified();

参考例子可以参考beetl自带的ResourceLoader

3.8. 使用CompositeResourceLoader

组合加载器,可以包含多个已有的ResourceLoader,如下代码将创建一个包含俩个文件和内存的ResourceLoader

FileResourceLoader fileLoader1 = new FileResourceLoader(path1);

FileResourceLoader fileLoader2 = new FileResourceLoader(path2);

Map data = getData();

// 根据id加载

MapResourceLoader mapLoader = new MapResourceLoader(data);

CompositeResourceLoader loader = new CompositeResourceLoader();

loader.addResourceLoader(new StartsWithMatcher("http:").withoutPrefix(), fileLoader2);

loader.addResourceLoader(new StartsWithMatcher("db:"), mapLoader);

loader.addResourceLoader(new AllowAllMatcher(), fileLoader1);

GroupTemplate gt = new GroupTemplate(loader, conf);

Template t = gt.getTemplate("/xxx.html");

如上例子,groupTemplate从CompositeResourceLoader里加载/xxx.html,由于http:和db:前缀都不匹配,因此,将实际采用fileLoader1加载path1+/xxx.html,如下是xxx.html文件内容

include("/xxx2.html"){}

include("http:/xxx.html"){}

%>

第2行仍然是由fileLoader1加载,但第3行以http:前缀开头,因此将fileLoader2加载path2+/xxx.html.xxx.html内容如下

include("db:1"){}

%>

因为以db:开头,因此会采用MapResourceLoader加载,内容是key为db:1对模板

3.9. 自定义错误处理器

错误处理器需要实现ErrorHandler接口的processExcption(BeetlException beeExceptionos, Writer writer);

beeExceptionos,模板各种异常

writer 模板使用的输出流。系统自带的并未采用此Writer,而是直接输出到控制台

自定义错误处理可能是有多个原因,比如

想将错误输出到页面而不是控制台

错误输出美化一下,而不是自带的格式

错误输出的内容做调整,如不输出错误行的模板内容,而仅仅是错误提示

错误输出到日志系统里

不仅仅输出日志,还抛出异常。默认自带的不会抛出异常,ReThrowConsoleErrorHandler 继承了ConsoleErrorHandler方法,打印异常后抛出

public class ReThrowConsoleErrorHandler extends ConsoleErrorHandler{

@Override

public void processExcption(BeetlException ex, Writer writer){

super.processExcption(ex, writer);

throw ex;

}

}

beetl 提供 ErrorInfo类来wrap BeetlException,转化为较为详细的提示信息,他具有如下信息

type 一个简单的中文描述

errorCode 内部使用的错误类型标识

errorTokenText 错误发生的节点文本

errorTokenLine 错误行

msg 错误消息,有可能没有,因为有时候errorCode描述的已经很清楚了

cause 错误的root 异常,也可能没有。

BeetlException 也包含了一个关键信息就是 resourceId,即出错所在的模板文件

3.10. 自定义安全管理器

所有模板的本地调用都需要通过安全管理器校验,默认需要实现NativeSecurityManager 的public boolean permit(String resourceId, Class c, Object target, String method) 方法

如下是默认管理器的实现方法

public class DefaultNativeSecurityManager implements NativeSecurityManager{

@Override

public boolean permit(String resourceId, Class c, Object target, String method){

if (c.isArray()){

//允许调用,但实际上会在在其后调用中报错。不归此处管理

return true;

}

String name = c.getSimpleName();

String pkg = c.getPackage().getName();

if (pkg.startsWith("java.lang")){

if (name.equals("Runtime") || name.equals("Process") || name.equals("ProcessBuilder")

|| name.equals("System")){

return false;

}

}

return true;

}

}

3.11. 注册全局共享变量

groupTemplate.setSharedVars(Map sharedVars)

3.12. 布局

布局可以通过Beetl提供的include,layout 以及模板变量来完成。模板变量能完成复杂的布局

采用layout include

//content.html内容如下:

layout("/inc/layout.html"){ %>

this is 正文

..........

如上一个子页面将使用layout布局页面,layout 页面内容如下

this is content:${layoutContent}

this is footer:

layoutContent 是默认变量,也可以改成其他名字,具体请参考layout标签函数

全局变量总是能被布局用的页面所使用,如果布局页面需要临时变量,则需要显示的传入,如:

var user= model.user;

include("/inc/header.html",{title:'这是一个测试页面',user:user}){}

%>

这样,title和user成为全局变量,能被header.html 及其子页面引用到

继承布局:采用模板变量和include

var jsPart = {

%>

web页面js部分

var htmlPart = {

%>

web页面html部分

include("/inc/layout.html",{jsSection:jsPart,htmlSection:htmlPart}){}

%>

layout.html页面如下:

${jsSection}

.......

${htmlSection}

3.13. 性能优化

Beetl性能已经很快了,有些策略能更好提高性能

使用二进制输出,此策略可以使模板在语法分析的时候将静态文本转化为二进制,省去了运行时刻编码时间,这是主要性能提高方式。但需要注意,此时需要提供一个二进制输出流,而不是字符流,否则性能反而下降

使用FastRuntimeEngine,默认配置。 此引擎能对语法树做很多优化,从而提高运行性能,如生成字节码来访问属性而不是传统的反射访问。关于引擎,可能在新的版本推出更好的引擎,请随时关注。

通过@type 来申明全局变量类型,这不能提高运行性能,但有助于模板维护

自定义ResourceLoader的isModified必须尽快返回,因此每次渲染模板的时候都会调用此方法

为什么Beetl性能这么好…………(待续)

3.14. 分布式缓存模板

Beetl模板引擎模板在同一个虚拟机里缓存Beetl 脚本。也可以将缓存脚本到其他地方,只要实现Cache接口,并设置ProgramCacheFactory.cache即可,这样GroupTemplate将从你提供的Cache中存取Beetl脚本

此功能未被很好测试

3.15. 定制输出

占位符输出允许定制。如所有日期类型都按照某个格式化输出,而不需显式的使用格式化输出,或者为了防止跨脚本站点攻击,需要对类型为String的值做检查等,不必使用格式化函数,可以直接对占位符输出进行定制,代码如下

PlaceholderST.output = new PlaceholderST.Output(){

@Override

public void write(Context ctx, Object value) throws IOException {

//定制输出

ctx.byteWriter.writeString("ok"+value!=null?value.toString:"");

}

};

如果PlaceholderST静态变量output 不为null,将使用output 来输出

3.16. 定制模板引擎

Beetl在线体验(http://ibeetl.com/beetlonline/)面临一个挑战,允许用户输入任何脚本做练习或者分享代码。但又需要防止用户输入恶意的代码,如

for(var i=0;i<10000000;i++){

//其他代码

}

%>

此时,需要定制模板引擎,遇到for循环的时候,应该限制循环次数,譬如,在线体验限制最多循环5次,这是通过定义替换GeneralForStatement类来完成的,这个类对应了for(exp;exp;exp) ,我们需要改成如下样子:

class RestrictForStatement extends GeneralForStatement{

public RestrictForStatement(GeneralForStatement gf){

super(gf.varAssignSeq, gf.expInit, gf.condtion, gf.expUpdate, gf.forPart, gf.elseforPart, gf.token);

}

public void execute(Context ctx){

if (expInit != null){

for (Expression exp : expInit){

exp.evaluate(ctx);

}

}

if (varAssignSeq != null){

varAssignSeq.execute(ctx);

}

boolean hasLooped = false;

int i = 0;

for (; i < 5; i++){

boolean bool = (Boolean) condtion.evaluate(ctx);

if (bool){

hasLooped = true;

forPart.execute(ctx);

switch (ctx.gotoFlag){

case IGoto.NORMAL:

break;

case IGoto.CONTINUE:

ctx.gotoFlag = IGoto.NORMAL;

continue;

case IGoto.RETURN:

return;

case IGoto.BREAK:

ctx.gotoFlag = IGoto.NORMAL;

return;

}

}else{

break;

}

if (this.expUpdate != null){

for (Expression exp : expUpdate){

exp.evaluate(ctx);

}

}

}

if (i >= 5){

try{

ctx.byteWriter.writeString("--Too may Data in loop,Ignore the left Data for Online Engine--");

ctx.byteWriter.flush();

} catch (IOException e){

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

@Override

public void infer(InferContext inferCtx){

super.infer(inferCtx);

}

}

尽管上面代码很复杂,但实际上是改写了原来的GeneralForStatement,将原来的24行while(true) 替换成for (; i < 5; i++) 用来控制最大循环,并且62行检测如果循环退出后,i等于5,则提示Too Many Data in Loop.

现在需要将此类替换原有的GeneralForStatement,

public class OnlineTemplateEngine extends DefaultTemplateEngine{

public Program createProgram(Resource resource, Reader reader, Map textMap, String cr,GroupTemplate gt){

Program program = super.createProgram(resource, reader, textMap, cr, gt);

modifyStatemetn(resource,program,gt);

return program;

}

private void modifyStatemetn(Resource resource,Program program,GroupTemplate gt){

Statement[] sts = program.metaData.statements;

StatementParser parser = new StatementParser(sts, gt, resource.getId());

parser.addListener(WhileStatement.class, new RestrictLoopNodeListener());

parser.addListener(GeneralForStatement.class, new RestrictLoopNodeListener());

parser.parse();

}

}

继承FastRuntimeEngine有所不同,因为改引擎会copy出一个脚本做分析优化,因此,俩个脚本都需要做修改

public class OnlineTemplateEngine extends FastRuntimeEngine{

public Program createProgram(Resource resource, Reader reader, Map textMap, String cr,GroupTemplate gt){

FilterProgram program = (FilterProgram)super.createProgram(resource, reader, textMap, cr, gt);

modifyStatemetn(resource,program,gt);

modifyStatemetn(resource,program.getCopy(),gt);

return program;

}

}

StatementParser 是关键类,他允许对模板的Program进行解析,并替换其中的Statement。parser.addListener 方法接受俩个参数,第一个是需要找的类,第二个是执行的监听器。

class RestrictLoopNodeListener implements Listener{

@Override

public Object onEvent(Event e){

Stack stack = (Stack) e.getEventTaget();

Object o = stack.peek();

if (o instanceof GeneralForStatement){

GeneralForStatement gf = (GeneralForStatement) o;

RestrictForStatement rf = new RestrictForStatement(gf);

return rf;

}else{

return null;

}

}

}

该监听器返回一个新的RestrictForStatement 类,用来替换来的GeneralForStatement。如果返回null,则不需替换。这通常发生在你仅仅通过修改该类的某些属性就可以的场景

完成这些代码后,在配置文件中申明使用新的引擎

ENGINE=org.bee.tl.online.OnlineTemplateEngine

这样就完成了模板引擎定制。

3.17. 直接运行Beetl脚本

Beetl模板本质上会转化为Beetl脚本来执行,这点跟jsp转为servlet来执行类似。GroupTemplate提供方法可以直接执行Beetl脚本

public Map runScript(String key, Map paras) throws ScriptEvalError

public Map runScript(String key, Map paras, Writer w) throws ScriptEvalError

public Map runScript(String key, Map paras, Writer w, ResourceLoader loader) throws ScriptEvalError

key为资源名,paras为脚本的全局变量,w可选参数,如果执行脚本有输出,则输出到w里,loader参数可选,如果指定,则使用此laoder加载脚本

执行脚本完毕后,返回到Map里的值可能包含如下:

模板的顶级的临时变量,key为临时变量名

return 值将返回到map里 ,key为return

如下脚本(此时就不需要脚本定界符了)

var a = 1;

var b = date();

var c = '2';

return a+1;

调用runScript后,map里将返回key分别为a,b,c,return。 值分别为1,当前日期,字符串'2,以及3。

beetl 获取html属性值,Beetl2.7.16中文文档(2)之高级功能相关推荐

  1. Beetl2.7.13 中文文档

    ## Beetl2.7.13 中文文档 ### 1. 什么是Beetl Beetl目前版本是2.7.13,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点.使 ...

  2. beetl 获取html属性值,关于beetl中list取值的问题

    报错 [DEBUG] 11:44:23.194 org.beetl.ext.nutz.LogErrorHandler.processExcption(LogErrorHandler.java:32) ...

  3. beetl 获取html属性值,Beetl学习 —— 绑定变量的HTML标签

    #### 2.24. 绑定变量的HTML标签 实现功能:Beetl支持将标签实现类(java代码)里的对象作为临时变量,被标签体引用. 如何实现: 第一步:自定义标签类继承GeneralVarTagB ...

  4. php xml获取标签属性,php获取xml属性值

    php获取xml属性值 $dom = new DOMDocument(); if (!$dom->load('attr.xml')) { echo "load books.xml fa ...

  5. 通过属性值从对象数组中获取JavaScript对象[重复]

    本文翻译自:Get JavaScript object from array of objects by value of property [duplicate] This question alr ...

  6. 无障碍开发(五)之设置获取无障碍属性值

    设置获取无障碍属性值 为了跨浏览器兼容,总是使用WAI-ARIA属性解析来访问和修改ARIA属性,例如object.setAttribute("aria-valuenow", ne ...

  7. php 获取css值,如何通过JS获取CSS属性值

    JS获取CSS属性 #f{background-color:#FF0000;} var o = document.getElementById('f'); document.write(o.style ...

  8. java 获取对象属性值为空或者非空的属性名称

    目录 1. 获取对象属性值为空的属性名称 2. 复制对象非空属性值 1. 获取对象属性值为空的属性名称 /** * @Title: getNullPropertyNames* @Description ...

  9. java高效获取内部类属性值_Java高级特性:内部类

    内部类是什么 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和 ...

最新文章

  1. php 如何利用 soap调用.Net的WebService asmx文件
  2. linux下IO口模拟I2C的一些总结
  3. 【LaTeX 语法】字母表示 ( 大写、小写、异体 希腊字母 | 粗体字母 | 花体字母 )
  4. [转]android使用shape stroke描边只保留底部
  5. thinkphp导入css失败,为什么用thinkphp总是提示css错误?
  6. 常用DBA SQL[转]
  7. PHP微信SDK——Zebra-Wechat
  8. 大数据服务社会的一个有益实践
  9. Sql自动配置器原理及其说明
  10. python02_python常用数据类型字符串
  11. 软件交接需要交接哪些内容_从代理记账公司交接,需要交接哪些财务资料?
  12. 在VB中调用API函数
  13. UFS 3.1协议分析(第一至四章) -- UFS概述
  14. qcloud php开发,GitHub - QcloudApi/qcloudapi-sdk-php
  15. 电脑用久了会变卡怎么办?让电脑变得流畅方法(不要等到蓝屏等无法挽救在着急)
  16. 电容式位移传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  17. onlyoffice5.4.2离线包的制作—解决中文字体问题
  18. 如何设置word表格中文字与表格线之间的距离(缩短文字和上面表格线之间距离),让表格变得紧凑
  19. ANSYS经典界面保存单元解和节点解
  20. Ubuntu系统出现kernel panic

热门文章

  1. U盘禁止写入/SD卡被禁用怎么解除
  2. 傅立叶逼近 matlab,傅里叶逼近.doc
  3. 手机变蓝牙音响_能提升幸福感的几大3C数码配件推荐?有线变无线、居家办公神器...
  4. web前端培训JS 运行机制的梳理
  5. ue4开关门点击时onclicked事件没有触发的解决方案
  6. 计算机图形学习课后习题解答--孔令得版
  7. Excel 有哪些可能需要熟练掌握而很多人不会的技能2
  8. 你必须知道的89个操作系统核心概念
  9. 黑苹果内置硬盘识别成外置硬盘_拥有希捷这款硬盘更加方便了我们的数据转移、保密和随时随地使用...
  10. 在家也可以享受购物体验?AR+Scene 帮您实现商品3D展示和虚拟试戴