第七章 类型转换:深入浅出struts2

第七章  类型转换

7.1 概述

  从页面输入的参数一般都是String类型,但相应的服务器获得参数时,需要的是各种不同的类型,因此,需要进行类型转换,struts提供了对类型转换的无缝支持。

  有时类型转换发生错误时(如输入日期,用户输入abcd,这时类型转换就不会成功),此时,如果某个动作类没有实现了validateAware接口,此时struts在遇到类型转换错误时仍会继续调用其动作方法,当作错误没有发生;如果某个动作类实现了validateAware 接口,struts在遇到类型转换错误时将不会继续调用其动作方法:struts将检查相关动作元素里的声明是否包含着一个input结果,如果是,则将控制权交给那个result元素,如果找不到,则抛出异常。

如: <action name="myAction" class="myAction">

           <result name="input">/jsp/Transaction.jsp</result>

           <result name="success">/jsp/receipts.jsp</jsp>

      </action>

此时myAction.java继承ActionSupport,间接地实现了ValidateAware接口,此时如果页面输入的参数不合法,则会转到Transactin.jsp页面。

7.2 类型转换错误消息的定制

一般的,如果输入的是错误格式信息,此时会用如下格式的提示信息Invalid field value for field fieldname,如输入日期错误,则提示Invalid field value for field "date"

该信息为默认的出错信息,可以修改,只要在相应的properties文件里写入invalid.fieldvalue.date=Please enter a date in  MM/dd/yyyy,此时,如果输入错误格式日期,则会提示“please enter a date in MM/dd/yyyy”,但需要注意的是,className.properties必须和className.java在一个子目录中, className是包含date的类的名字。

除了可以定制出错信息,还可以定义出错信息的格式,每条出错信息都被打包在一个HTML span元素里,只要覆盖其中行标为.errorMessage的那个CSS样式来改变出错信息样式即可。如希望出错信息为红色字体显示,此时可以在该JSP页面里写上

<style>
.errorMessage{
color:red
}
</style>

7.3  类型转换器的定制

 即意味着可以自己编写类型转换函数,自定义的类型转换器必须实现ognl.TypeConverter 接口或者对这个接口的实现(ognl.DefaultTypeConverter,  org.apache.structs2.util.StrutsTypeConverter)进行扩展。

TypeConverter接口只有一个方法:java.lang.Object convertValue(java.util.Map context, java.lang.Object targer, java.lang.reflect.Member member, java.lang.String propertyName, java.lang.Object value,java.lang.Class toType);

其中:context:将在其中进行类型转换的OGNL上下文环境, 包含对于Value Stack栈和各种资源的引用

          target:将在其中对有关属性进行设置的目标对象

         member: 将被设置的类成员(构造器、方法或字段)的名字

        propertyName:将被设置的属性的名字

             value:将被转换的值

        toType:转换结果的类型

获取valueStack栈:ValueStack stack=(ValueStack)context.get(ValueStack.VALUE_STACK);这样就可以用findValue方法去检索某个属性的值

获取ServletContex,HttpServletRequest, HttpServletResponse可通过

context.get(StructsStatics.SERVLET_CONTEXT);

context.get(StructsStatics.HTTP_REQUEST);

context.get(StructsStatics.HTTP_RESPONSE);

对ServletContext 里的参数可以在web.xml里面配置,如

<context-param>
  <param-name>datePattern</param-name>
   <param-value>yyyy-MM-dd</param-value>
</context-param>

 为了让一个自定义的类型转换器发挥作用,还需要为所支持的每一种类型转换编写相应的代码。如:

public Object convertValue(Map context,Object target,Member member,String propertyName,Object value,Class toType){
if(toType==String.class){
// convert from double to Stirng and return the result;
 }else if(toType==Double.class||toType==Double.TYPE){
//convert from string to double and return the result;
}
return null;
}

与自行实现的TypeConverter接口相比,对DefaultTypeConverter类进行扩展更容易,如:

import java.util.Map;
import java.lang.reflect.Member;

public class DefaultTypeConverter implements TypeConverter{
public Object convertValue(Map contet,Object target,Member member,String propertyName,Object value,Class toType){
return convertValue(context,value,toType);
}
public Object convertValue(Map context, Object value,Class toType){
return OgnlOps.convertValue(value,toType);
}
}

7.3.1 对自定义类型转换器的配置

两种方法,一种是基于字段,一种是基于类

对于基于字段的配置方法,可以为某个动作的各个属性分别指定一个自定义的转换器,具体的说,先要创建一个文件ActionClass-conversion.properties,其中ActionClass是相关动作类的名字,里面的内容为:fileld1=converter1 field2=converter2 ....请注意,这个配置文件必须和相关的动作类在一个子目录里,如,对于名为User类的date, name进行转换,date字段运用转换器converter1,  name运用转换器converter2, 此时配置文件为User-conversion.properties, 内容为date=converter1  name=converter2 
对于基于类的配置方法,即为每个类定义一个转换器,类里面的各个属性都用这个转换器,具体的说,在WEB-INF/classes目录下创建一个conversion.properties文件,内容为完整类名=converter....如设置User运用转换器converter,则内容为User=converter

7.4扩展StructsTypeConverter类

该抽象类有两个方法:convertFromString ,convertToString,使得能够有选择的转换,如下所示:

import java.lang.reflect.Member;
import java.util.Map;

import ognl.DefaultTypeConverter;

public abstract class StrutsTypeConverter extends DefaultTypeConverter{
	/* (non-Javadoc)
	 * @see ognl.DefaultTypeConverter#convertValue(java.util.Map, java.lang.Object, java.lang.Class)
	 */
	@Override
	public Object convertValue(Map context, Object value, Class toType) {
		if(toType.equals(String.class)){
			return convertToString(context,value);
		}else if(value instanceof String[]){
			return convertFromString(context,(String[])value,toType);
		}else if(value instanceof String){
			return convertFromString(context,new String[]{(String)value},toType);
		}else{
			return performFallbackConversion(context,value,toType);
		}
		
	}
   public abstract Object convertToString(Map context, Object value);
   
   public abstract Object convertFromString(Map context, String[] value, Class toType);
   
   public abstract Object convertFromString(Map context, String value, Class toType);
   
   protected Object performFallbackConversion(Map context, Object value, Class toType){
	   return super.convertValue(context, value, toType);
   }
}
注意看代码里的红色部分。
例子:将一个color对象转换成string类型并完成反向转换的转换器

Color类

package app07c;

import com.opensymphony.xwork2.ActionSupport;

public class Color extends ActionSupport{
private int red;
private int green;
private int blue;
/**
 * @return the red
 */
public int getRed() {
	return red;
}
/**
 * @param red the red to set
 */
public void setRed(int red) {
	this.red = red;
}
/**
 * @return the green
 */
public int getGreen() {
	return green;
}
/**
 * @param green the green to set
 */
public void setGreen(int green) {
	this.green = green;
}
/**
 * @return the blue
 */
public int getBlue() {
	return blue;
}
/**
 * @param blue the blue to set
 */
public void setBlue(int blue) {
	this.blue = blue;
}

//返回给定基色的十六进制成分值
//Integer.toHexString(int i),将i转换成十六进制
public String getHexCode(){
	return (red<16?"0":"")+Integer.toHexString(red)+(green<16?"0":"")+Integer.toHexString(green)+(blue<16?"0":"")+Integer.toHexString(blue);
}
}
Design类

package appl7c;
import com.opensymphony.xwork2.ActionSupprot;
public class Design extends ActionSupport{
private String designName;
private Color color;
//getter and setter
}

MycolorConverter类

package app07c;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

import com.opensymphony.xwork2.util.TypeConversionException;

public class MyColorConverter extends StrutsTypeConverter{

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		boolean ok=false;
		String rgb=values[0];
		//String.split(String regex):该函数是用给定的正则表达式拆分该字符串,如3,20,124(","),则会返回{"3","20","124"}这个字符串数组
		String[] colorComponents=rgb.split(",");
		if(colorComponents!=null&&colorComponents.length==3){
			String red=colorComponents[0];
			String green=colorComponents[1];
			String blue=colorComponents[2];
			
			int redCode=0;
			int greenCode=0;
			int blueCode=0;
			
			try{
				redCode=Integer.parseInt(red.trim());
				greenCode=Integer.parseInt(green.trim());
				blueCode=Integer.parseInt(blue.trim());
				
				if(redCode>=0&&redCode<256&&greenCode>=0&&greenCode<256&&blueCode>=0&&blueCode<256){
					Color color=new Color();
					color.setRed(redCode);
					color.setGreen(greenCode);
					color.setBlue(blueCode);
					ok=true;
					return color;
				}
		}catch(NumberFormatException e){
			
		}
		if(!ok){
			throw new TypeConversionException("Invalid color codes");
		}
		}
		return null;
		}

	@Override
	public String convertToString(Map context, Object o) {
		Color color=(Color)o;
		return color.getRed()+","+color.getGreen()+","+color.getBlue();
	}

}


配置文件:将Color类映射到MyColorConverter

xwork-conversion.properties

app07c.Color=app07c.converter.MyColorConverter

7.5与复杂对象的使用

即将表单字段映射到多个对象的不同属性,使用OGNL可以做到

如:一个表单对应一个Admin的action, Admin有两个属性,一个ID,一个Employee类,Employee类有一个name和birthDate属性,将表单的字段映射到id,employee如下:

	<s:form action="Admin">
	  <s:textfield name="ID" />
	  <s:textfield name="employee.name" />
	  <s:textfield name="employee.birthDate" />
	</s:form>

如果为birthDate指定转换器,则在Admin-conversion.properties里注册即可

employee.birthDate=app07d.converter.MyDateConverter

7.6 与collection配合使用

即表单里可以一次添加多条信息(如添加员工,要输入员工firstname,lastname,birthDate),此时表单与action相对应时,action里的属性employee为collection类型

如例子所示:

Admin类

import java.util.Collection;
import com.opensymphony.xwork2.ActionSupport;

public class Admin extends ActionSupport{
private Collection<Employee> employees;

public Collection<Employee> getEmployees() {
	return employees;
}
public void setEmployees(Collection<Employee> employees) {
	this.employees = employees;
}
}


Employee类里有属性firstname,lastname,birthDate

public class Employee {
private String firstname;
private String lastname;
private String birthDate;

//getter and setter
}

 Admin.jsp

	<table>
	<tr>
		<td><s:textfield name="employees[0].firstname" /></td>
		<td><s:textfield name="employees[0].lastname" /></td>
		<td><s:textfield name="employees[0].birthDate" /></td>
	</tr>
	<tr>
		<td><s:textfield name="employees[1].firstname" /></td>
		<td><s:textfield name="employees[1].lastname" /></td>
		<td><s:textfield name="employees[1].birthDate" /></td>
	</tr>
	</table>

提交表单时,employees[0]将为employees里的第一个元素

输出表单时,可以直接用iterator,里面的value属性为employees即可

注:上面的例子是硬性的每次只能输入两名员工的信息,动态的一次输入多个员工信息的实现如下:

<s:iterator value="new int[#parameters.count[0]]" status="stat">
<tr>
<td><s:textfield  name="%{'employees['+stat.index+'].firstname'}" /></td>
<td><s:textfield name="%{'employees['+stat.index+'].lastname'}" />
</td>
<td><s:textfield
	name="%{'employees['+stat.index+'].birthDate'}" /></td>
</tr>
</s:iterator>

注:如果要静态规定输入员工的信息数,可以在iterator的value属性为new int[4],如果想使用用户传过来的参数,则如上所示,但上面的[0]不可少,因为parameters总是返回一个string数组,而不是一个String,

请注意嵌在末尾的count请求参数http://localhost:8080/Admin.action?count=n

7.7 与MAP配合使用

这一节没什么特别,唯一一点:如employees为MAP类型,要取得其分键值的集合可用如下:

<s:iterator value="employees.keySet()" var="key" status="stat">
<ul>
<li><s:property value="#key" />:
    <s:property value="employees[#key].firstname" />
    <s:property value="employees[#key].lastname" />
</li>
</ul>
</s:iterator>





 


 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值