Struts入门学习笔记(三)——数据转换
一.基于OGNL的类型转换
1. 针对复合类的类型转换
借助内置的类型转换器,Struts2可以完成字符串和基本类型之间的类型转换,除此之外借助OGNL表达式的支持,Struts2允许另一种简单的方式将请求参数转换成复合类型,首先我们先看一下这样一个action.java
package cn.green.action;
import cn.green.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute(){
if(getUser().getName().equals("tom") && getUser().getPass().equals("1234")){
addActionMessage("转换成功");
return SUCCESS;
}
addActionMessage("转换失败");
return ERROR;
}
}
以下是input.jsp表单:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<s:form action="login">
<s:textfield name="user.name" label="用户名:" />
<s:textfield name="user.pass" label="密 码:" />
<tr>
<td colspan="2">
<s:submit value="转换" theme="simple" />
<s:reset calue="重置" theme="simple"></s:reset>
</td>
</tr>
</s:form>
</body>
</html>
这里就不写在struts.xml中配置的信息了,但我们写出以上的形式之后很容易发现程序是可以运行的,我们知道基本数据类型是可以默认转换的,那么这种User类的实例又是为什么自己自动完成装配了呢,就是因为OGNL表达式,具体应用如上所示,改变的是在输入表单input.jsp中的写法,然后它会自动地将表单中user.name的值赋值给action中的user实例的name变量,以此类推,最后就可以在action中直接用了。
这里有几个问题需要注意,因为这种方式其实就是运用反射来创建复合类User,所以必须为他提供无参构造器,除此之外在action中必须配备复合类的get、set方法
2. 针对Collction或Map的转换
同样我们这里直接贴代码,主要不同点在于jsp页面的name标签写法而已,action与上面相同,即可实现自动转换。
input.jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<s:form action="login">
<s:textfield name="users['one'].name" label="第一个用户名:" />
<s:textfield name="users['one'].pass" label="第一个密 码:" />
<s:textfield name="users['two'].name" label="第二个用户名:" />
<s:textfield name="users['two'].pass" label="第二个密 码:" />
<tr>
<td colspan="2">
<s:submit value="转换" theme="simple" />
<s:reset calue="重置" theme="simple"></s:reset>
</td>
</tr>
</s:form>
</body>
</html>
执行的action如下:
package cn.green.action;
import java.util.Map;
import cn.green.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private Map<String, User> users;
public Map<String, User> getUsers() {
return users;
}
public void setUsers(Map<String, User> users) {
this.users = users;
}
public String execute(){
if(getUsers().get("one").getName().equals("tom") && getUsers().get("one").getPass().equals("1234")){
addActionMessage("登陆成功");
return SUCCESS;
}
addActionMessage("登陆失败");
return ERROR;
}
}
当然,我们需要访问这些action中的属性时,也可以用以下的方式:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
第一个用户名:<s:property value="users['one'].name"/><br>
第一个密 码:<s:property value="users['two'].name"/><br>
第二个用户名:<s:property value="users['one'].pass"/><br>
第二个密 码:<s:property value="users['two'].pass"/><br>
</body>
</html>
3. 针对List类型的转换
其实道理和上面都一样,只不过方式换成了List而已,List可以依据顺序去提取和赋值:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<s:form action="login">
<s:textfield name="users[0].name" label="第一个用户名:" />
<s:textfield name="users[0].pass" label="第一个密 码:" />
<s:textfield name="users[1].name" label="第二个用户名:" />
<s:textfield name="users[1].pass" label="第二个密 码:" />
<tr>
<td colspan="2">
<s:submit value="转换" theme="simple" />
<s:reset calue="重置" theme="simple"></s:reset>
</td>
</tr>
</s:form>
</body>
</html>
其他省略,都是一样的。
二.指定集合元素的类型
我们知道实现上面第一种的原理就是泛型和反射,通过泛型实现了关键步骤,Struts2还允许另外一种方式去实现它,通过一个叫类型转换文件(*.properties)的方式,请看下面的action:
package cn.green.action;
import java.util.List;
import java.util.Map;
import cn.green.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
-
- private List users;
public List getUsers() {
return users;
}
public void setUsers(List users) {
this.users = users;
}
@Override
public String execute() throws Exception {
System.out.println(getUsers());
User firstUser = (User) getUsers().get(0);
if(firstUser.getName().equals("jerry") && firstUser.getPass().equals("1234")){
addActionMessage("登陆成功");
return SUCCESS;
}
addActionMessage("登陆失败");
return ERROR;
}
}
当然,如果只是凭借上面的写法,程序运行时还是不知道该怎么转,这里就需要一个配置文件去写入定义的类型才行(局部类型转换文件),这个文件有着专门的写法:ActionName-conversion.properties,这里我们写成LoginAction-conversion.properties,将文件放到与action同一个目录下才行,为了指定List里面元素的数据类型,我们是这样定义的:
List集合属性的名称 List集合里元素的类型
Element_<ListPropName>=<ElementType>
本例中这样书写:
Element_users = cn.green.bean.User
这样的话程序有可以正常运行了,达到了相同的效果。
如果是Map类型,由于它属于key-value性质,我们可以这样写:
<!--指定key-->
key_<MapPropName>=<KeyType>
<!--指定value-->
Element_<MapPropName>=<VauleType>
三.自定义类型转换器
还是接着上面的讲,LoginAction使用如下:
package cn.green.action;
import cn.green.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute(){
if(getUser().getName().equals("tom") && getUser().getPass().equals("1234")){
addActionMessage("转换成功");
return SUCCESS;
}
addActionMessage("转换失败");
return ERROR;
}
}
但是表单提交却不是按照要求填写的,如果我们从表单接受的仅仅是一个字符串,如tom,1234 的样式,那我们如何封装想要的复合类型,我们知道机器不可以自己去给你用逗号分开,所以需要我们自己做一个类型转换器,在里面告诉它怎么转,这个自定义的类型转换器要有一定的规则,必须继承自DefaultTypeConverter:
package cn.green.converter;
import java.util.Map;
import cn.green.bean.User;
import ognl.DefaultTypeConverter;
public class UserConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map context, Object value, Class toType) {
//字符串向User类型转换
if(toType == User.class){
String[] params = (String[]) value;
User user = new User();
String[] userValues = params[0].split(",");
user.setName(userValues[0]);
user.setPass(userValues[1]);
return user;
}
//User类型向字符串转换
else if(toType == String.class){
User user = (User) value;
return "<"+user.getName()+","+user.getPass()+">";
}
reutrn null;
}
}
注意一点,写转换器时是双向的,从A->B,也从B->A
四.注册类型转换器
以上我们定义了自己的类型转换器,但是可以直接用么?当然不是,Struts2不知道怎么去用它们,我们需要将这些类型转换器注册在web应用中,这样才能应用。
Struts2有三种注册方式:
注册局部类型转换器:仅对某个action的属性起作用
注册全局类型转换器:对所有action的特定类型的属性都会生效
使用jdk1.5的注解来注册类型转换器
1.局部类型转换器
只需要在局部类型转换器中加入下列即可:本例中
<propName>=<ConverterName>
本例中我们这样设置即可:
<!--执行action的user属性需要使用UserConverter类来完成转换-->
user=cn.green.converter.UserConverter
2.全局类型转换器
全局类型转换器不是对指定Action起作用,而是对指定类型起作用,注册全局类型转换文件xwork-conversion.properties,将其放到根目录下即可
同样的注册写入:
<propType>=<ConvertClass>
本例中我们注册如下:
cn.green.bean.User=cn.green.converter.UserConverter
五.基于Struts2的自定义类型转换器
Struts2中给我们放了一个strutsTypeConverter抽象类,他其实就是
DefaultTypeConverter的子类,只不过默认的需要自己去判断转换成哪种类型,而这个直接有方法调用,具体如下:
package cn.green.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import cn.green.bean.User;
public class UserConverterByStruts extends StrutsTypeConverter{
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
User user = new User();
String[] userValues = values[0].split(",");
user.setName(userValues[0]);
user.setPass(userValues[1]);
return user;
}
@Override
public String convertToString(Map context, Object value) {
User user = (User) value;
return "<"+user.getName()+","+user.getPass()+">";
}
}
注册方式和上面的一样,这里不再赘述。
六.处理Set集合
此处不详细介绍了,大致方法如上,唯一需要注意的就是由于Set的
无序性会造成一些额外的设置。
七.类型转换中的错误处理
1.处理类型转换错误
Struts自带默认的转换错误拦截器,单为了让Strtuts2类型转换的错误处理机制生效,包括【输入检验】生效,都必须让Action继承Struts2 的ActionSupport基类,因为这个基类负责收集类型转换错误,输入校验错误,并将它们封装到FieldError对象,添加到ActionContext中,这样做之后我们可以很容易的在视图层使用<s:fielderror/>标签输出相应的错误
此处如果想添加对中文错误提示的信息,可在国际化资源文件中加入下列代码:
xwork.default.invalid.fieldvalue={0}
相应错误提示:
2.处理集合属性的转换错误
大致原理如上,只不过对OGNL的应用可以多一些。