从浏览器获取的HttpP参数值均为字符串,而实际所需数据可能是字符串、数字、布尔、日期时间或JavaBean类型,因此需将 HTML 表单数据转换为到一个 Action所需类型。

Struts2系列:(8)类型转换_ERP

Parameters 拦截器负责struts2 中的请求参数与 action 属性的映射, 是默认的 defaultStack 拦截器中的一种. 

Struts2提供强大的类型转换支持,不仅提供内置的类型转换器,还可自定义类型转换器

Struts2类型转换支持OGNL表达式,只需要在表单控件的name属性使用ognl表达式即可,比如表单控件的name属性为user.name,则此控件的数据将进入赋值给Action的user属性中的name属性;


对于这里提到的Parameters拦截器,我个人认为是struts-default.xml中定义的拦截器:

<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

通过查看源代码,得到以下信息:

ParametersInterceptor
全名:com.opensymphony.xwork2.interceptor.ParametersInterceptor
(1)这个拦截器用于设置value stack上的参数
This interceptor sets all parameters on the value stack.
(2)这个拦截器获取值是通过ActionContext.getParameters()方法,设置值通过ValueStack.setValue(String, Object)。
This interceptor gets all parameters from ActionContext.getParameters() and sets them on the value stack by calling ValueStack.setValue(String, Object), typically resulting in the values submitted in a form request being applied to an action in the value stack. Note that the parameter map must contain a String key and often containers a String[] for the value.

 


1、内置类型转换


1)基本数据类型,例如int、long、boolean、float、double等;

2)日期类型(java.util.Date), 使用当前区域(Locale)的短格式转换,即    DateFormat.getInstance(DateFormat.SHORT);

3)集合(Collection)类型,将request.getParameterValues(String arg)返回的字符串数据与java.util.Collection转换;

4)集合(Set)类型、List;

5)数组(Array)类型, 将字符串数组的每一个元素转换成特定的类型,并组成一个数组。


Action属性一定要有getter和setter方法。



2、自定义类型转换器


所有的Struts 2.0中的转换器都必须实现ognl.TypeConverter接口,OGNL包提供了ognl.DefaultTypeConverter类和StrutsTyperConverter。(第4部分对此进行了一些补充



DefaultTypeConverter

public Object convertValue(ActionContext context,Object value,Class toType);

    完成双向转换,即从String数组转到Action属性及Action属性转到String;需要注意的是从String数组转到Action属性,而不是String转到Action属性;如果只有一个字符串,则取params[0]即可;

StrutsTypeConverter

     StrutsTypeConverter是DefaultTypeConverter的子类,DefaultTypeConverter在一个方法中进行双向转换,而StrutsTypeConverter将两个方向分别用两个函数实现。

 Object convertFromString(Map context, String[] values, Class toClass)   
 String convertToString(Map context, Object o)


2.1、自定义类型转换的基本步骤

a)自定义一个类(需与String转换的类)--------转换的目标类型


b)定义一个类转换器(继承StrutsTypeConverter) -------谁来转换

继承StrutsTypeConverter类时,有两个方法需要覆写。

public Object convertFromString(Map context, String[] values, Class toClass)
public String convertToString(Map context, Object o)


类型转换,是在处理用户输入参数时调用,发生在调用action的setXXX方法之前。

convertFromString方法的values参数与request.getParameterValues(参数名)获取值一致

values是一个字符串数组而不是一个字符串,因为在表单中有可能具有相同name的表单域。



在页面中使用:

<%@taglib uri="/struts-tags" prefix="s" %>

<s:property value=""/>


c)注册类转换器

类型转换器注册后才能使用 。注册可基于字段或类。


方式一:基于字段配置(局部): 为某个Action的属性制定自定义转换器. 

1.创建一属性文件: Action类名-conversion.properties, 与该Action类放同一个目录下

2.添加键值对:属性名称=类型转换器的全类名


方式二:基于类配置(全局) 

1.在src目录下创建 xwork-conversion.properties 文件

2. 添加键值对:待转换的类型=类型转换器的全类名


2.2、案例

a)自定义一个类Point.java(类型转换的目标类型)

package com.rk.strut.e_typeconvert;

public class Point
{
	private int x;
	private int y;
	private int z;
	
	public int getX()
	{
		return x;
	}
	public void setX(int x)
	{
		this.x = x;
	}
	public int getY()
	{
		return y;
	}
	public void setY(int y)
	{
		this.y = y;
	}
	public int getZ()
	{
		return z;
	}
	public void setZ(int z)
	{
		this.z = z;
	}
}

b)定义一个类转换器PointConverter.java

package com.rk.strut.e_typeconvert;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

public class PointConverter extends StrutsTypeConverter
{

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass)
	{
		String str = values[0];
		String[] coordinates = str.split(",");
		int x = Integer.parseInt(coordinates[0]);
		int y = Integer.parseInt(coordinates[1]);
		int z = Integer.parseInt(coordinates[2]);
		
		Point p = new Point();
		p.setX(x);
		p.setY(y);
		p.setZ(z);
		
		return p;
	}

	@Override
	public String convertToString(Map context, Object o)
	{
		Point p = (Point) o;
		return (p.getX() + "," + p.getY() + "," + p.getZ());
	}

}

c)注册类转换器(全局,位于src目录下):xwork-conversion.properties

com.rk.strut.e_typeconvert.Point=com.rk.strut.e_typeconvert.PointConverter

d)输入页面(前台-浏览器)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>输入界面</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/rk/typeconvert/point.action" method="post">
    	请输入两个三维坐标点:<br/>
    	<input type="text" name="firstPoint"/>
    	<input type="text" name="secondPoint"/>
    	<input type="submit" value="提交"/>
    </form>
  </body>
</html>

e)后台Action类和struts.xml配置(后台-服务器)

package com.rk.strut.e_typeconvert;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class PointAction extends ActionSupport
{
	private Point firstPoint;
	private Point secondPoint;
	public Point getFirstPoint()
	{
		return firstPoint;
	}
	public void setFirstPoint(Point firstPoint)
	{
		this.firstPoint = firstPoint;
	}
	public Point getSecondPoint()
	{
		return secondPoint;
	}
	public void setSecondPoint(Point secondPoint)
	{
		this.secondPoint = secondPoint;
	}
	@Override
	public String execute() throws Exception
	{
		HttpServletRequest request = ServletActionContext.getRequest();
		double distance = getDistance();
		request.setAttribute("distance", distance);
		return super.execute();
	}
	
	public double getDistance()
	{
		int x1 = firstPoint.getX();
		int y1 = firstPoint.getY();
		int z1 = firstPoint.getZ();
		
		int x2 = secondPoint.getX();
		int y2 = secondPoint.getY();
		int z2 = secondPoint.getZ();
		
		int delta_x = x1 - x2;
		int delta_y = y1 - y2;
		int delta_z = z1 - z2;
		
		return Math.sqrt(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z);
	}
	
}

struts-typeconvert.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
    <package name="typeconvertPackage" namespace="/rk/typeconvert" extends="struts-default">
        <action name="point" class="com.rk.strut.e_typeconvert.PointAction">
            <result name="success">/typeconvert/success.jsp</result>
        </action>
    </package>	
</struts>


f)输出结果页面(前台-浏览器)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>计算结果</title>

	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
  </head>
  
  <body>
   第1个坐标是:<s:property value="firstPoint"/><br>
   第2个坐标是:<s:property value="secondPoint"/><br>
   两个坐标之间距离是: ${distance }<br>
  </body>
</html>


3、补充


下边的这些内容,还不是特别懂。先记下来。


集合类型转换(List,Map,Set)

List:

    Element_变量名=元素的类型,

     页面中使用方法:<s:property value="user[0].name" />

Map:

    Key_变量名=键的类型,  Element_变量名=元素的类型

   页面中使用方法:<s:property value="user['key'].name" />

Set :

    KeyProperty_setName = 用于索引元素的属性,Element_setName=元素的类型

    页面中使用方法:<s:property value="user('keyProperty').name" /> 


public class BookConfirmAction extends ActionSupport {

private List<Book> books;

public List<Book> getBooks() {

return books;

}


public String execute() throws Exception {

return "success";

}

public void setBooks(List<Book> books) {

this.books = books;

}

在BookConfirmAction-conversion.properties文件中:

    Element_books=com.future.struts2.typeconversion.model.list.Book


4、源代码阅读

在2的部分提到“OGNL包提供了ognl.DefaultTypeConverter类和StrutsTyperConverter”,这句话是从教师的PPT上拷下来的,感觉不太对。


我在ognl包中看到了TypeConverter接口和DefaultTypeConvert类,但没有StrutsTypeConverter类。

后我又找到这几个:

com.opensymphony.xwork2.conversion.TypeConverter

它的描述是:Interface for accessing the type conversion facilities within a context. This interface was copied from OGNL's TypeConverter

com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter

它的描述是:Default type conversion. Converts among numeric types and also strings. Contains the basic type mapping code from OGNL.

org.apache.struts2.util.StrutsTypeConverter

它的描述是:Base class for type converters used in Struts. This class provides two abstract methods that are used to convert both to and from strings -- the critical functionality that is core to Struts's type coversion system.

TypeConverter接口源码:

package com.opensymphony.xwork2.conversion;

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

/**
 * 
 * Interface for accessing the type conversion facilities within a context.
 * 
 * This interface was copied from OGNL's TypeConverter
 * 
 */
public interface TypeConverter
{
    /**
       * Converts the given value to a given type.  The OGNL context, target, member and
       * name of property being set are given.  This method should be able to handle
       * conversion in general without any context, target, member or property name specified.
       * @param context context under which the conversion is being done
       * @param target target object in which the property is being set
       * @param member member (Constructor, Method or Field) being set
       * @param propertyName property name being set
       * @param value value to be converted
       * @param toType type to which value is converted
       * @return Converted value of type toType or TypeConverter.NoConversionPossible to indicate that the
                 conversion was not possible.
     */
    public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
    
    public static final Object NO_CONVERSION_POSSIBLE = "ognl.NoConversionPossible";
    
    public static final String TYPE_CONVERTER_CONTEXT_KEY = "_typeConverter";
}


DefaultTypeConverter源码:

package com.opensymphony.xwork2.conversion.impl;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.conversion.TypeConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;

import java.lang.reflect.Array;
import java.lang.reflect.Member;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * Default type conversion. Converts among numeric types and also strings.  Contains the basic 
 * type mapping code from OGNL.
 * 
 */
public abstract class DefaultTypeConverter implements TypeConverter {

    protected static String MILLISECOND_FORMAT = ".SSS";

    private static final String NULL_STRING = "null";

    private static final Map<Class, Object> primitiveDefaults;

    private Container container;

    static {
        Map<Class, Object> map = new HashMap<Class, Object>();
        map.put(Boolean.TYPE, Boolean.FALSE);
        map.put(Byte.TYPE, Byte.valueOf((byte) 0));
        map.put(Short.TYPE, Short.valueOf((short) 0));
        map.put(Character.TYPE, new Character((char) 0));
        map.put(Integer.TYPE, Integer.valueOf(0));
        map.put(Long.TYPE, Long.valueOf(0L));
        map.put(Float.TYPE, new Float(0.0f));
        map.put(Double.TYPE, new Double(0.0));
        map.put(BigInteger.class, new BigInteger("0"));
        map.put(BigDecimal.class, new BigDecimal(0.0));
        primitiveDefaults = Collections.unmodifiableMap(map);
    }

    @Inject
    public void setContainer(Container container) {
        this.container = container;
    }

    public TypeConverter getTypeConverter( Map<String, Object> context )
    {
        Object obj = context.get(TypeConverter.TYPE_CONVERTER_CONTEXT_KEY);
        if (obj instanceof TypeConverter) {
            return (TypeConverter) obj;
            
        // for backwards-compatibility
        } else if (obj instanceof ognl.TypeConverter) {
            return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
        }
        return null; 
    }

    public Object convertValue(Map<String, Object> context, Object target, Member member,
            String propertyName, Object value, Class toType) {
        return convertValue(context, value, toType);
    }

    public Object convertValue(Map<String, Object> context, Object value, Class toType) {
        return convertValue(value, toType);
    }

    /**
     * Returns the value converted numerically to the given class type
     * 
     * This method also detects when arrays are being converted and converts the
     * components of one array to the type of the other.
     * 
     * @param value
     *            an object to be converted to the given type
     * @param toType
     *            class type to be converted to
     * @return converted value of the type given, or value if the value cannot
     *         be converted to the given type.
     */
    public Object convertValue(Object value, Class toType) {
        Object result = null;

        if (value != null) {
            /* If array -> array then convert components of array individually */
            if (value.getClass().isArray() && toType.isArray()) {
                Class componentType = toType.getComponentType();

                result = Array.newInstance(componentType, Array
                        .getLength(value));
                for (int i = 0, icount = Array.getLength(value); i < icount; i++) {
                    Array.set(result, i, convertValue(Array.get(value, i),
                            componentType));
                }
            } else {
                if ((toType == Integer.class) || (toType == Integer.TYPE))
                    result = Integer.valueOf((int) longValue(value));
                if ((toType == Double.class) || (toType == Double.TYPE))
                    result = new Double(doubleValue(value));
                if ((toType == Boolean.class) || (toType == Boolean.TYPE))
                    result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
                if ((toType == Byte.class) || (toType == Byte.TYPE))
                    result = Byte.valueOf((byte) longValue(value));
                if ((toType == Character.class) || (toType == Character.TYPE))
                    result = new Character((char) longValue(value));
                if ((toType == Short.class) || (toType == Short.TYPE))
                    result = Short.valueOf((short) longValue(value));
                if ((toType == Long.class) || (toType == Long.TYPE))
                    result = Long.valueOf(longValue(value));
                if ((toType == Float.class) || (toType == Float.TYPE))
                    result = new Float(doubleValue(value));
                if (toType == BigInteger.class)
                    result = bigIntValue(value);
                if (toType == BigDecimal.class)
                    result = bigDecValue(value);
                if (toType == String.class)
                    result = stringValue(value);
                if (Enum.class.isAssignableFrom(toType))
                    result = enumValue((Class<Enum>)toType, value);
            }
        } else {
            if (toType.isPrimitive()) {
                result = primitiveDefaults.get(toType);
            }
        }
        return result;
    }

    /**
     * Evaluates the given object as a boolean: if it is a Boolean object, it's
     * easy; if it's a Number or a Character, returns true for non-zero objects;
     * and otherwise returns true for non-null objects.
     * 
     * @param value
     *            an object to interpret as a boolean
     * @return the boolean value implied by the given object
     */
    public static boolean booleanValue(Object value) {
        if (value == null)
            return false;
        Class c = value.getClass();
        if (c == Boolean.class)
            return ((Boolean) value).booleanValue();
        // if ( c == String.class )
        // return ((String)value).length() > 0;
        if (c == Character.class)
            return ((Character) value).charValue() != 0;
        if (value instanceof Number)
            return ((Number) value).doubleValue() != 0;
        return true; // non-null
    }
    
    public Enum<?> enumValue(Class toClass, Object o) {
        Enum<?> result = null;
        if (o == null) {
            result = null;
        } else if (o instanceof String[]) {
            result = Enum.valueOf(toClass, ((String[]) o)[0]);
        } else if (o instanceof String) {
            result = Enum.valueOf(toClass, (String) o);
        }
        return result;
    }

    /**
     * Evaluates the given object as a long integer.
     * 
     * @param value
     *            an object to interpret as a long integer
     * @return the long integer value implied by the given object
     * @throws NumberFormatException
     *             if the given object can't be understood as a long integer
     */
    public static long longValue(Object value) throws NumberFormatException {
        if (value == null)
            return 0L;
        Class c = value.getClass();
        if (c.getSuperclass() == Number.class)
            return ((Number) value).longValue();
        if (c == Boolean.class)
            return ((Boolean) value).booleanValue() ? 1 : 0;
        if (c == Character.class)
            return ((Character) value).charValue();
        return Long.parseLong(stringValue(value, true));
    }

    /**
     * Evaluates the given object as a double-precision floating-point number.
     * 
     * @param value
     *            an object to interpret as a double
     * @return the double value implied by the given object
     * @throws NumberFormatException
     *             if the given object can't be understood as a double
     */
    public static double doubleValue(Object value) throws NumberFormatException {
        if (value == null)
            return 0.0;
        Class c = value.getClass();
        if (c.getSuperclass() == Number.class)
            return ((Number) value).doubleValue();
        if (c == Boolean.class)
            return ((Boolean) value).booleanValue() ? 1 : 0;
        if (c == Character.class)
            return ((Character) value).charValue();
        String s = stringValue(value, true);

        return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
        /*
         * For 1.1 parseDouble() is not available
         */
        // return Double.valueOf( value.toString() ).doubleValue();
    }

    /**
     * Evaluates the given object as a BigInteger.
     * 
     * @param value
     *            an object to interpret as a BigInteger
     * @return the BigInteger value implied by the given object
     * @throws NumberFormatException
     *             if the given object can't be understood as a BigInteger
     */
    public static BigInteger bigIntValue(Object value)
            throws NumberFormatException {
        if (value == null)
            return BigInteger.valueOf(0L);
        Class c = value.getClass();
        if (c == BigInteger.class)
            return (BigInteger) value;
        if (c == BigDecimal.class)
            return ((BigDecimal) value).toBigInteger();
        if (c.getSuperclass() == Number.class)
            return BigInteger.valueOf(((Number) value).longValue());
        if (c == Boolean.class)
            return BigInteger.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
        if (c == Character.class)
            return BigInteger.valueOf(((Character) value).charValue());
        return new BigInteger(stringValue(value, true));
    }

    /**
     * Evaluates the given object as a BigDecimal.
     * 
     * @param value
     *            an object to interpret as a BigDecimal
     * @return the BigDecimal value implied by the given object
     * @throws NumberFormatException
     *             if the given object can't be understood as a BigDecimal
     */
    public static BigDecimal bigDecValue(Object value)
            throws NumberFormatException {
        if (value == null)
            return BigDecimal.valueOf(0L);
        Class c = value.getClass();
        if (c == BigDecimal.class)
            return (BigDecimal) value;
        if (c == BigInteger.class)
            return new BigDecimal((BigInteger) value);
        if (c.getSuperclass() == Number.class)
            return new BigDecimal(((Number) value).doubleValue());
        if (c == Boolean.class)
            return BigDecimal.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
        if (c == Character.class)
            return BigDecimal.valueOf(((Character) value).charValue());
        return new BigDecimal(stringValue(value, true));
    }

    /**
     * Evaluates the given object as a String and trims it if the trim flag is
     * true.
     * 
     * @param value
     *            an object to interpret as a String
     * @return the String value implied by the given object as returned by the
     *         toString() method, or "null" if the object is null.
     */
    public static String stringValue(Object value, boolean trim) {
        String result;

        if (value == null) {
            result = NULL_STRING;
        } else {
            result = value.toString();
            if (trim) {
                result = result.trim();
            }
        }
        return result;
    }

    /**
     * Evaluates the given object as a String.
     * 
     * @param value
     *            an object to interpret as a String
     * @return the String value implied by the given object as returned by the
     *         toString() method, or "null" if the object is null.
     */
    public static String stringValue(Object value) {
        return stringValue(value, false);
    }

    protected Locale getLocale(Map<String, Object> context) {
        Locale locale = null;
        if (context != null) {
            locale = (Locale) context.get(ActionContext.LOCALE);
        }
        if (locale == null) {
            locale = container.getInstance(LocaleProvider.class).getLocale();
        }
        return locale;
    }

}

StrutsTypeConverter源码:

package org.apache.struts2.util;

import java.util.Map;

import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;

/**
 * <!-- START SNIPPET: javadoc -->
 *
 * Base class for type converters used in Struts. This class provides two abstract methods that are used to convert
 * both to and from strings -- the critical functionality that is core to Struts's type coversion system.
 *
 * <p/> Type converters do not have to use this class. It is merely a helper base class, although it is recommended that
 * you use this class as it provides the common type conversion contract required for all web-based type conversion.
 *
 * <p/> There's a hook (fall back method) called <code>performFallbackConversion</code> of which
 * could be used to perform some fallback conversion if <code>convertValue</code> method of this
 * failed. By default it just ask its super class (Ognl's DefaultTypeConverter) to do the conversion.
 *
 * <p/> To allow the framework to recognize that a conversion error has occurred, throw an XWorkException or
 * preferable a TypeConversionException.
 *
 * <!-- END SNIPPET: javadoc -->
 *
 */
public abstract class StrutsTypeConverter extends DefaultTypeConverter {
    public Object convertValue(Map context, Object o, Class toClass) {
        if (toClass.equals(String.class)) {
            return convertToString(context, o);
        } else if (o instanceof String[]) {
            return convertFromString(context, (String[]) o, toClass);
        } else if (o instanceof String) {
            return convertFromString(context, new String[]{(String) o}, toClass);
        } else {
            return performFallbackConversion(context, o, toClass);
        }
    }

    /**
     * Hook to perform a fallback conversion if every default options failed. By default
     * this will ask Ognl's DefaultTypeConverter (of which this class extends) to
     * perform the conversion.
     *
     * @param context
     * @param o
     * @param toClass
     * @return The fallback conversion
     */
    protected Object performFallbackConversion(Map context, Object o, Class toClass) {
        return super.convertValue(context, o, toClass);
    }


    /**
     * Converts one or more String values to the specified class.
     *
     * @param context the action context
     * @param values  the String values to be converted, such as those submitted from an HTML form
     * @param toClass the class to convert to
     * @return the converted object
     */
    public abstract Object convertFromString(Map context, String[] values, Class toClass);

    /**
     * Converts the specified object to a String.
     *
     * @param context the action context
     * @param o       the object to be converted
     * @return the converted String
     */
    public abstract String convertToString(Map context, Object o);
}