Struts2 类型转换 Type Convertion


为什么会有类型转换?
HTTP协议中传递的任何内容都是String类型的,所以一旦我们在服务器上需要一个非String类型的对象,例如:int或者Date,那么我们就需要在收到 HTTP请求的数据的时候,首先将String类型的数据变换为我们需要的对应类型的数据,之后再使用。这个过程就是类型转换
类型转换在Struts2中是透明的,即Struts2内置了类型转换机制。
转换原理:
以一个例子来说明如何使用Struts2内置的类型转换功能。加入我们希望用户在画面上输入一个字符形式的坐标点,例如(33,2)而我们希望在程序中得到一个Point(33, 2)的类型与之对应。
要想达到上面的功能我们需要一个名字位:ActionName-conversion.properties的文件,在文件中定义Action中的属性和画面字段之间的转换关系。例如:
point = com.jpleasure.convertor.PointConverter
也就是说画面一个叫做point的项目(input类型,name为point)提交到服务器上之后,在向Action中的point属性赋值之前需要使用 PointConverter将字符串转换为Point类,在Action中的point属性向画面显示的时候需要使用PointConverter将 Point类转换为字符串类型。
其中PointConverter需要实现ognl.TypeConverter接口。TypeConverter有两个接口,一个负责将字符串转变为对象类型,另一个负责将对象类型转换为字符串类型,分别对应着内容的提交和显示。
有些时候我们希望所有的Point类在默认的情况下使用PointConverter来转换,这时候我们需要定义全局的Converter类。这可以在xwork-conversion.properties文件中定义,例如:
com.jpleasure.Point = com.jpleasure.convertor.PointConverter
在Struts2中提供了一个TypeConverter接口的默认实现:
org.apache.struts2.action.util.StrutsTypeConverter
这个类有两个默认的抽象转换方法和performFallbackConversion,performFallbackConversion方法负责处理类型转换出错的处理。
在自定义TypeConverter的时候,可以实现TypeConverter接口,之后编写TypeConverter的转换方法,也可以从 StrutsTypeConverter继承而来,StrutsTypeConverter本身实现了TypeConverter接口,并且实现了基本的转换方法。
内建的转换:
Struts2内建了对以下类型的转换的支持:
String
boolean / Boolean
char / Character
int / Integer, float / Float, long / Long, double / Double
dates - 使用HTTP 请求对应地域(Locale)的SHORT形式转换字符串和日期类型。
arrays -每一个字符串内容可以被转换为不同的对象
collections - 转换为Collection类型,默认为ArrayList类型,其中包含String类型。
对于Array类型和Collection类型,需要对其中的每一个元素进行单独的转换。
自定义TypeConverter:
使用如下的代码自定义需要的TypeConverter
public class MyConverter extends StrutsTypeConverter {
public Object convertFromString(Map context, String[] values, Class toClass) {
.....
}
public String convertToString(Map context, Object o) {
.....
}
}
为了让Struts2框架发现类型转换的错误,需要在出错的情况下在上述的两个方法中抛出XWorkException或者TypeConversionException。

我们使用一个例子来展现如何实现TypeConvertor类型:

// Point 类
package com.jpleasure.conversion;


public class Point {
private int x;
private int y;

public Point() {

}

public Point(int x, int y) {
this.x = x;
this.y = y;
}

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 String toString() {
StringBuffer sb = new StringBuffer("Point(");
sb.append(x).append(", ").append(y).append(")");
return sb.toString();
}
}

// PointConvertor 类
package com.jpleasure.conversion;

import org.apache.struts2.util.StrutsTypeConverter;

import java.util.Map;


public class PointConvertor extends StrutsTypeConverter {

// 从字符串转换为对象的方法。
public Object convertFromString(Map map, String[] strings, Class aClass) {
if (strings.length > 0) {
String pointStr = strings[0];
String[] pointStrArray = pointStr.split(",");
if (pointStrArray.length == 2) {
Point p = new Point();
p.setX(Integer.parseInt(pointStrArray[0]));
p.setY(Integer.parseInt(pointStrArray[1]));
return p;
} else {
return null;
}

} else {
return null;
}
}

// 从对象转换为字符串的方法。
public String convertToString(Map map, Object o) {
if (o instanceof Point) {
return o.toString();
} else {
return "";
}
}
}
// 测试用PointAction类
package com.jpleasure.action;
import com.jpleasure.conversion.Point;
import com.opensymphony.xwork2.ActionSupport;

public class PointAction extends ActionSupport {
private Point point;

public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public String execute() {
return SUCCESS;
}
}
// JSP文件




Point Page




// PointAction配置文件类。
/point/Point.jsp/point/Point.jsp
复杂的类型转换:
(1)处理Null值
有些时候我们会被NullPointerException搞的焦头烂额,为什么系统不能为我们定义了但是没有初始化的对象建立一个空的Object的引用呢?Struts2有这个功能,但是在默认情况下Struts2关闭了这个功能,要想开启这个功能,需要在ParameterInterceptor开始处理参数之前在ValueStack中将一个值开启,这个值是:InstantiatingNullHandler.CREATE_NULL_OBJECTS。
在Java代码中InstantiatingNullHandler.CREATE_NULL_OBJECTS的值是:xwork.NullHandler.createNullObjects
创建空值对象的规则为:
* 如果属性声明为Collection或List, 将返回一个ArrayList并赋值给空引用.
* 如果属性声明为Map, 将返回一个HashMap并赋值给空引用.
* 如果空值属性是一个带有无参构造函数的简单Bean, 将使用ObjectFactory.buildBean(java.lang.Class, java.util.Map)方法创建一个实例.

(2)Collection和Map
简单List转换
//JSP代码




Point Page



//Action代码
package com.jpleasure.action;

import com.jpleasure.conversion.Point;
import com.opensymphony.xwork2.ActionSupport;

import java.util.List;


public class PointAction extends ActionSupport {
private Point point;
private List references;

public Point getPoint() {
return point;
}

public void setPoint(Point point) {
this.point = point;
}

public List getReferences() {
return references;
}

public void setReferences(List references) {
this.references = references;
}

public String execute() {

if(references == null) {
System.out.println("references is null");
} else {
System.out.println("references length is:" + references.size());

for(Object s: references) {
System.out.println("" + s);
}
}

return SUCCESS;
}
}

对象类型List转换(key-value pair 方式)
// Person 类型
package org.apache.struts2.showcase.conversion;
import java.io.Serializable;

public class Person implements Serializable {
private String name;
private Integer age;

public void setName(String name) { this.name = name; }
public String getName() { return this.name; }

public void setAge(Integer age) { this.age = age; }
public Integer getAge() { return this.age; }
}

// PersionAction 类型
package org.apache.struts2.showcase.conversion;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;

public class PersonAction extends ActionSupport {

private List persons;

public List getPersons() { return persons; }
public void setPersons(List persons) { this.persons = persons; }
public String input() throws Exception {
return SUCCESS;
}
public String submit() throws Exception {
return SUCCESS;
}
}
// PersonAction转化配置文件PersonAction-conversion.properties
# PersonAction中persons属性(List类型)中元素的类型
Element_persons=org.apache.struts2.showcase.conversion.Person
// JSP部分代码

其中stat记录了当前循环的信息,其中stat.index表示当前循环的下标。
所以上述代码会生成如下的代码:

对象类型List转换(value 方式)
// Address 类型
package org.apache.struts2.showcase.conversion;

public class Address {

private String id;
private String address;

public String getId() { return id; }
public void setId(String id) { this.id = id; }

public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }

}
// AddressAction 类型
package org.apache.struts2.showcase.conversion;
import java.util.LinkedHashSet;
import java.util.Set;
import com.opensymphony.xwork2.ActionSupport;

public class AddressAction extends ActionSupport {

private Set addresses = new LinkedHashSet();

public Set getAddresses() { return addresses; }
public void setAddresses(Set addresses) { this.addresses = addresses; }
public String input() throws Exception {
return SUCCESS;
}

public String submit() throws Exception {
System.out.println(addresses);
return SUCCESS;
}
}
//AddressAction转换配置文件AddressAction-conversion.properties
KeyProperty_addresses=id
Element_addresses=org.apache.struts2.showcase.conversion.Address
CreateIfNull_addresses=true
// JSP代码

上述代码会转换为:

<s:form action="submitAddressInfo" namespace="/conversion">
<s:textfield label="Address 0"
name="addresses('id0')" />
<s:textfield label="Address 1"
name="addresses('id1')" />
<s:textfield label="Address 2"
name="addresses('id2')" />
<s:submit />
</s:form>
------------------------------------------------------------------------------------------

基本类型可以完成自动转换,主要转换对象类型,基于OGNL

分为:
----局部类型转换
1.自定义转换类继承DeaultTypeConverter
重写convertValue(Map context, Object value, Class toType)
Xxx.class == toType (或用equals()) String --> Object
String.class == toType Object --> String
2.或继承StrutsTypeConverter
重写
Object convertFromString(Map context,String[] values,Class toClass)
String convertToString(Map context, Object o)
3.配置文件:
Action类名-conversion.properties 需要和对应Action在同一目录下
内容:
属性名=转换类名 (多个属性就写多行)
----全局类型转换
1. 2. 同局部转换
3.配置文件:
xwork-conversion.properties(固定的)
内容:
需要类型转换的Action的全称类名=转换类名
【局部类型转换】
=======================================
1. input.jsp
<h1>用逗号将点的两个坐标分割开</h1> 如:20,30
<s:form action="pointConverter">
<s:textfield name="point" label="点" />
<s:textfield name="age" label="年龄" />
<s:textfield name="username" label="用户名" />
<s:textfield name="date" label="生日" />
<s:submit label="提交" />
</s:form>

对于一个属性来说,先进行类型转化,转化成功了再进行输入验证
=======================================
2.建立 模型类
.....test.bean 包
public class Point {
private int x;
private int y;
//setter...getter...方法
}
=======================================
3.简要说明
用于实现转换的TypeCenverter接口:
主要使用DefaultTypeCenverter类
重写convertValue()方法
public Object convertValue(Map context, Object value, Class toType)
=======================================
4.建立自定义转换类
建立....test.converter包
建立 转换类
需要继承DeaultTypeConverter,DeaultTypeConverter实现了TypeConverter转换接口
public class PointConverter extends DeaultTypeConverter {
@Override
public Object convertValue(
Map context, //上下文
Object value, //要转换的值(String数组)
Class toType //目标类型(转换成什么)
) {
//如果要转换的类型是Point
if( Point.class == toType ) {
Point point = new Point();
String[] str = (String[])value;

//此处数组只有一个值,在0的位置上
String[] paramValues = str[0].split(",");
int x = Integer.parseInt(paramValues[0]);
int y = Integer.parseInt(paramValues[1]);
point.setX(x);
point.setY(y);
return point;
}
//如果要转换的类型为String (也可以用系统自动转换)
if( String.class == toType ) {

Point point = (Point)value;
int x = point.getX();
int y = poitn.getY();
String result = "[x=" + x + " , y=" + y + "]";
return result;
}
return null;
}

注:为什么value是数组,因为页面的字段可以有同样的名字,如:
<s:textfield name="username" />
<s:textfield name="username" />
这样的话,获得username时就应该是多个,所以是字符串数组
=======================================
5.建立action
public class PointAction extends ActionSupport {
private Point point;
private int age;
private String username;
private Date date;
//setter...getter...
@Override
public String execute() throws Execption {
return SUCCESS;
}
}
--------------------
补充:
Action接口中:
Field Summary 字段摘要
ERROR 执行失败
INPUT 验证没有成功
LOGIN 没有执行成功,因为没有登陆
NONE 执行成功,但不相识任何题图
SUCCESS 执行成功
=======================================
6. struts.xml
<package name="struts2" extends="struts-default">
<action name="pointConverter" class="com.test.action.PointAction">

<result name="success">/output.jsp</result>
</action>
</package>
=======================================
7.output.jsp
<%@ taglib prefix="s" uri="/strurs-tags" %>
property标签自动调用value对应的字段的get方法,value="point"和getPoint()方法匹配
point: <s:property value="point"/><br/>
age:<s:property value="age"/><br/>
username:<s:property value="username"/><br/>
date:<s:property value="date"/><br/>
${point }<br/>
${point.x }<br/>
${point.y }
=======================================
8.定义属性文件--用于指定转换类
该文件必须和对应的转换类在同一个包下
PointAction-conversion.properties
PointAction: 对应哪个Action中的属性进行转换
-conversion.properties: 是固定不变的
PointAction-conversion.properties的内容
对那个属性进行转换=用那个类对其进行转换,多个属性就写多行
point=com....converter.PointConverter
【完成】
=======================================
整体流程
1.提交请求->到struts.xml中去找对应的action,这里是pointConverter,找到之后,知道了有指定的类(PointAction)来处理请求
2.生成PointAction的实例,到PointAction中,把请求中的值调用set方法赋给该类的每个属性
3.当调用set方法前,首先检查,对于这个属性有没有自定义的类型转换,没有的时候就按照系统的行为进行默认的转换;
如果发现已经定义好了类型转换(检查PiontAction的目录下有没有PointAction-onversion.properties存在),然后到该.properties文件里找,你到底要通过哪个类转换哪一个属性。
4.到相应的转换类中,这里是PointConverter,然后判断转换的方向,
Poitn.class==toType:通过字符串数组 转换到 Point对象,进入到相应的if流程
return后,流程回到PointAction中的setPoint方法,然后使用转换后的对象赋值给属性
5.将所有的属性赋值成功后,执行execute方法,然后通过返回值找到struts.xml中的action中的result对应的页面(output.jsp),显示结果。
6.然后发现<s:property>,每找到value,就到PointAction中调用相应的get方法,查找该字段有没有配置自动转换类,对于没有字段,应用系统默认的转换后直接返回。对于配置的,流程同上,到PointConverter中执行对应的if流程(String.class==toType),return后回到PointAction中的get方法返回其转换后的值,显示到页面。
【全局类型转换】
=======================================
需求:3个点的坐标
=======================================
多个属性的局部类型转换
input.jsp中
添加两个点的坐标
<s:textfield name="point2" label="点2" />
<s:textfield name="point3" label="点3" />
PointAction中
添加两个点字段
private Point point2;
private Point point3;
//setter...getter...
PointAction-conversion.properties中
添加
point2=com........converter.PointConverter
point3=com........converter.PointConverter
result.jsp中
添加
点2:<s:property value="point2" /><br/>
点3:<s:property value="point3" /><br/>
=======================================
需求:要转换的对象分布在多个action里面,上面的方式比较麻烦
应使用全局的类型转换:对系统里所有满足要求的进行转换
=======================================
配置文件:
配置文件名:xwork-conversion.properties(固定的)
应在classes目录下,也就是struts.xml相同的目录
放在src下就可以了
内容:
要转化那个类的全称类名=使用哪个类进行转换
com.scorpio.jh.struts2.test.bean.Point=com.scorpio.jh.struts2.test.converter.PointConverter
用PointConverter类对系统中所有的需要转换的类进行转换
=======================================
通过继承util包下的StrutsTypeConverter类进行转换
=======================================
1. 建立一个新的转换类,继承StrutsTypeConverter
public class PointConverter2 extends StrutsTypeConverter {
//从String转换到目标对象
@Override
public Object convertFromString(
Map context,
String[] values,
Class toClass) {
Point point = new Point();
String[] paramValues = values[0].split(",");
int x = Integer.parseInt(paramValues[0]);
int y = Integer.parseInt(paramValues[1]);

point.setX(x);
point.setY(y);

return point;
}
//从对象转换到String
@Override
public String convertToString( Map context, Object o ) {

Point point = (Point)o;
int x = point.getX();
int y = point.getY();

String result = "[x=" + x + ", y=" + "]";
return result;
}
}
=======================================
2.修改配置文件 xwork-conversion.properties
com.scorpio.jh.struts2.test.bean.Point=com.scorpio.jh.struts2.test.converter.PointConverter2

【完成】
拓展:
=======================================
需求:Action中用一个集合类型,存储Piont对象
=======================================
1.action类
public class PointAction extends ActionSupport {
private List<Piont> point;
private int age;
private String username;
private Date date;
//setter...getter...
public String execute() throws Exception { ... }

}
=======================================
2.input.jsp
<s:form action="pointConverter">
<s:textfield name="point" label="点1" /> name相同,会返回一个数组
<s:textfield name="point" label="点2" />
<s:textfield name="point" label="点3" />
<s:textfield name="age" label="年龄" />
<s:textfield name="username" label="用户名" />
<s:textfield name="date" label="生日" />
<s:submit label="提交" />
</s:form>
=======================================
3. result.jsp
point: <s:property value="point"/><br/>
age:<s:property value="age"/><br/>
username:<s:property value="username"/><br/>
date:<s:property value="date"/><br/>
=======================================
4.转换类 PointConverter3
public class PointConverter3 extends StrutsTypeConverter {
//从String到对象
@Override
public Object convertFromString(
Map context,
String[] values,
Class toClass) {
List<Point> list = new ArrayList<Point>();
for( String value : values ) {
Point point = new Point();
String[] paramValues = value.split.split(",");
int x = Integer.parseInt(paramValues[0]);
int y = Integer.parseInt(paramValues[1]);
point.setX(x);
point.setY(y);
list.add(point);
}
return list;
}
//从对象到String
@Override
public String convertToString( Map context, Object o ) {

List<Point> list = (List<Point>)o;

//StringBuilder 非同步版本的StringBuffer
StringBuilder sb = new StringBuilder();
sb.append("[");
int number = 0;

for( Point point : list ) {

++number;
int x = point.getX();
int y = point.getY();
sb.append(number).append(" x=").append(x).append(", y=").append(y).append(" ");
}
sb.append("]");
return sb.toString();
}
}
=======================================
5.采用局部转换
PointAction-conversion.properties
point=com....converter.PointConverter3
【完成】
=======================================
注:
如果需要转换的字段没有使用泛型 List<Point>
则要在局部类型转换中加入 Element_ 头 + 你要转换的东西
但是建议使用泛型
=======================================
补充:
可以用 属性名.属性的值 这样一种方式赋值
<s:form action="pointConverter">
<s:textfield name="point.x" label="x坐标" /> *********
<s:textfield name="point.y" label="y坐标" /> *********
<s:textfield name="age" label="年龄" />
<s:textfield name="username" label="用户名" />
<s:textfield name="date" label="生日" />
<s:submit label="提交" />
</s:form>
------------------------
public class PointAction extends ActionSupport {
private Piont point;
private int age;
private String username;
private Date date;
//setter...getter...
public String execute() throws Exception { ... }

}
------------------------
将全局和局部类型转换注释掉 用#号
------------------------
重写Point的toString()
pubic String toString() {
return "[x=" + x + ", y=" + y + "]";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值