论坛首页 Java版 Webwork

对于moxie的"WebWork教程"补充 - [类型转化]

浏览 17012 次
该帖已经被评为精华帖
作者 正文
时间:2005-01-26
moxie的WebWork教程 ( http://forum.javaeye.com/viewtopic.php?t=5964 ) 非常详细,但是好像还缺少了webwork其他一些很有特色的东西,我在这里补充一些上来,首先是类型转化:

我们知道由于HTTP协议只能传递String,如果后台的Java模型使用了其他类型的属性,必须得做一些转化工作,而这些转化代码通常却是繁琐而又无聊,WebWork提供了Type Conversion功能,能够让我们从这些代码中解脱出来。

看一个例子,

一个Person对象,有一个出生日期的属性:
[code:1]
public class Person {
private Date dateOfBirth;
}
[/code:1]

一个Action对Person做操作:
[code:1]
public class CreatePerson implements Action {
private Person person;

public String execute() throws Exception {
//Do some work
//...
return SUCCESS;
}

}
[/code:1]

我们通常在页面上会限制这个日期的格式(比如yyyy-MM-dd),通过写一个WebWork的转化器,我们就可以把在页面上:<input type="text" name="person.dateOfBirth" value="1949-10-01"/> 这样的值直接变成了相应的Date对象。

转换器需要扩展ognl.DefaultTypeConverter (在webwork2.1.6以后,我们可以扩展WebWorkTypeConverter这个对象,更新的代码可以参考这个: http://cvs.javaeye.com:8008/quake/getfile/jert/src/java/com/javaeye/core/webwork/converter/DateConverter.java?v=1.2 )
[code:1]
public class DateConverter extends DefaultTypeConverter {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

public Object convertValue(Map ognlContext, Object value, Class toType) {
Object result = null;
if (toType == Date.class) {
result = doConvertToDate(value);
} else if (toType == String.class) {
result = doConvertToString(value);
}
return result;
}

private Date doConvertToDate(Object value) {
Date result = null;

if (value instanceof String) {
try {
result = sdf.parse((String) value);
} catch (ParseException e) {
throw new XworkException("Could not parse date", e);
}
} else if (value instanceof Object[]) {
// let's try to convert the first element only
Object[] array = (Object[]) value;
if ((array != null) && (array.length >= 1)) {
value = array[0];
result = doConvertToDate(value);
}
} else if (Date.class.isAssignableFrom(value.getClass())) {
result = (Date) value;
}
return result;
}

private String doConvertToString(Object value) {
String result = null;
if (value instanceof Date) {
result = sdf.format(value);
}
return result;
}
}
[/code:1]

那么WebWork是如何知道要使用这个转化器对Person的dateOfBirth做转化呢?我们有2种方法:
1. Class-specific conversion rules
写一个className-conversion.properties,这上面的例子里,我们就在Person.java所在的package下面,写一个Person-conversion.properties
在这个文件里面指定:
dateOfBirth=com.javaeye.core.webwork.converter.DateConverter

2. Application-wide conversion rules
在classpath root下面写一个xwork-conversion.properties:
java.util.Date=com.javaeye.core.webwork.converter.DateConverter

这样一旦写好了一个转换器以后,就能够很方便地被重用了,在这篇wiki里面,有更加详细的介绍: http://wiki.opensymphony.com/display/XW/Type+Conversion

再举一个实际的例子:
在jert里面,我们希望可以给用户选择多语言,那么我们在页面上提供了一个下拉列表,选项有zh_CN和en_US,通过一个LocaleConverter,我们就可以直接把String转化成Locale对象:

http://cvs.javaeye.com:8008/quake/getfile/jert/src/java/com/javaeye/core/webwork/converter/LocaleConverter.java?v=1.1

结论:灵活使用转换器可以减少我们那些繁琐无聊的类型转化代码。
   
时间:2005-02-28
这个DateConverter没有默认的实现? 好像简单问题复杂化了
   
0 请登录后投票
时间:2005-03-01
如果你不自己设置DateConvert,你的date格式为new SimpleDateFormat()
   
0 请登录后投票
时间:2005-03-03
moxie 写道
如果你不自己设置DateConvert,你的date格式为new SimpleDateFormat()

好,我试试
   
0 请登录后投票
时间:2005-03-05
如果有一个类,有很多子类,我可以在参数里指定Action的自动使用某个子类型吗?

class Child {}

class Son extends Child

class Daughter extends Child

class SomeAction {
Child child;
}


我可以在form里通过某个filed指定child的实例应该用Son或者Daughter类创建吗?特别是当Child为abstract的时候,我们肯定必须使用某个子类的。
   
0 请登录后投票
时间:2005-03-05
指定类型的问题解决了,通过写了一个扩展自WebWorkTypeConverter的自定义转换器,然后结合form里把指定的属性设置为需要转型的子类型而完成的。
我在xwork-default-conversions.properties里配置了一个全局的类型转换器。(不知道为什么,Action级别的根据field的配置始终行不通,是不是这个文件里不能配置成someField.someObject=xxx.converter?)

转换器的核心代码如下:
[code:1]
/* (non-Javadoc)
* @see com.opensymphony.webwork.util.WebWorkTypeConverter#convertFromString(java.util.Map, java.lang.String[], java.lang.Class)
*/
public Object convertFromString(Map context, String[] values, Class toClass) {
try {
return Class.forName(values[0]).newInstance();
} catch (Exception e) {
return values;
}
}

/* (non-Javadoc)
* @see com.opensymphony.webwork.util.WebWorkTypeConverter#convertToString(java.util.Map, java.lang.Object)
*/
public String convertToString(Map context, Object o) {
return o.getClass().getName();
}
[/code:1]
部分标单如下:
[code:1]
<ww:select label="'Select One Discount Type'" name="'coupon.discount'"
list="#{'com.alpaq.icoupon.core.entity.discount.AmountDiscount':'Amount Discount', 'com.alpaq.icoupon.core.entity.discount.PercentageDiscount':'Percent Discount'}" onchange="'switchDiscountType(this);'"/>

<ww:textfield label="'Amount Discount Value'" name="'coupon.discount.amount'" />
<ww:textfield label="'Percent Discount Value(without %)'" id="typePDInput" name="'coupon.discount.percent'" disabled="'true'"/>

[/code:1]
不过,我发现这么一来虽然能把类型转换成期望的子类型,但是,该类型的field还是不能被设置。如以上标单中的coupon.discount.amount,并不能设置action里coupon属性的discount属性的amount属性。。。


另外,我还单独模仿Jert里的PrametersInterceptor写了一个Interceptor来吧coupon.discount放到coupon.discount.amount的前头,不然会出现异常。
   
0 请登录后投票
时间:2005-03-06
问问Quake Wang:
如果我在界面上有两种格式的日期怎么办?
例如一种短格式: SimpleDateFormat("yyyy-mm-dd"),一种长格式SimpleDateFormat("yyyy-mm-dd hh:MM:ss")
   
0 请登录后投票
时间:2005-03-06
To yufan_shi
我把回复你的email贴这里了,不知道其他人是否有其他方法:

首先如果你有多种Coupon的话,那么应该有一个CouponFactory,能够根据不同的
值,创建出不同的Coupon实例,比如: DefaultCoupon,ElectricCoupon等等, 那
么可以按照jert里的例子,用一个Intercetpor来首先绑定couponType(当然也可以
是couponTypeId这样的属性名,这样就可以重用jert里的那个Interceptor了):
[code:1]
Action.setCouponType(String couponType) { this.coupon =
CouponFactory.getCoupon(couponType);
}
[/code:1]
对于discount的多态,我们可以在action里面添加helper method:
[code:1]
setDiscountAmount(Double amout) {
((AmountDiscount) coupon.getDiscount()).setAmout(amout);
}
[/code:1]
这样可以去掉if else的判断,但是需要在action多写helper method, 我觉得这个
问题,你也可以去webwork的mailist问问看,看看其他人是否有好的解决方法。
   
0 请登录后投票
时间:2005-03-06
To wolfsquare
你说的这个问题,可以根据你的应用情况,看哪种方式是比较常见的转换规则,那么把这个规则定成
引用

2. Application-wide conversion rules
在classpath root下面写一个xwork-conversion.properties:
java.util.Date=com.javaeye.core.webwork.converter.DateConverter


另外的一个转换,可以写成Class-specific conversion rules :
otherDate=com.javaeye.core.webwork.converter.OtherDateConverter
   
0 请登录后投票
时间:2005-03-06
Quake Wang 写道
To yufan_shi
我把回复你的email贴这里了,不知道其他人是否有其他方法:

首先如果你有多种Coupon的话,那么应该有一个CouponFactory,能够根据不同的
值,创建出不同的Coupon实例,比如: DefaultCoupon,ElectricCoupon等等, 那
么可以按照jert里的例子,用一个Intercetpor来首先绑定couponType(当然也可以
是couponTypeId这样的属性名,这样就可以重用jert里的那个Interceptor了):
[code:1]
Action.setCouponType(String couponType) { this.coupon =
CouponFactory.getCoupon(couponType);
}
[/code:1]
对于discount的多态,我们可以在action里面添加helper method:
[code:1]
setDiscountAmount(Double amout) {
((AmountDiscount) coupon.getDiscount()).setAmout(amout);
}
[/code:1]
这样可以去掉if else的判断,但是需要在action多写helper method, 我觉得这个
问题,你也可以去webwork的mailist问问看,看看其他人是否有好的解决方法。

想了一下,也许可以通过spring注入一个discountFactory,并且在spring里配置好TypeID和ClassName的对应关系,然后,就能通过discountFactory动态的创建子类型了,以后要增加什么新的子类型,也不需要修改任何Java代码,只要改一下spring的配置文件。
对于这些属性对象的属性的动态绑定,可能只能采用你说的这种方法;现在我的本本不再我手上,还没有试过在改成用factory在setTypeId()里绑定子类型的方法后,用coupon.discount.amount能否成功绑定.
   
0 请登录后投票
论坛首页 Java版 Webwork

跳转论坛:
JavaEye推荐