论坛首页 Java企业应用论坛

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

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

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

看一个例子,

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


一个Action对Person做操作:
public class CreatePerson implements Action {
    private Person person;
    
    public String execute(); throws Exception {
        //Do some work
        //...
        return SUCCESS;
    }

}


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

转换器需要扩展ognl.DefaultTypeConverter (在webwork2.1.6以后,我们可以扩展WebWorkTypeConverter这个对象,更新的代码可以参考这个: http://cvs.iteye.com:8008/quake/getfile/jert/src/java/com/javaeye/core/webwork/converter/DateConverter.java?v=1.2 )
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;
    }
}


那么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.iteye.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?)

转换器的核心代码如下:
    /* (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();;
    }

部分标单如下:
<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'"/>


不过,我发现这么一来虽然能把类型转换成期望的子类型,但是,该类型的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了):
Action.setCouponType(String couponType); { this.coupon =
CouponFactory.getCoupon(couponType);;
}

对于discount的多态,我们可以在action里面添加helper method:
setDiscountAmount(Double amout); {
((AmountDiscount); coupon.getDiscount(););.setAmout(amout);;
}

这样可以去掉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了):
Action.setCouponType(String couponType); { this.coupon =
CouponFactory.getCoupon(couponType);;
}

对于discount的多态,我们可以在action里面添加helper method:
setDiscountAmount(Double amout); {
((AmountDiscount); coupon.getDiscount(););.setAmout(amout);;
}

这样可以去掉if else的判断,但是需要在action多写helper method, 我觉得这个
问题,你也可以去webwork的mailist问问看,看看其他人是否有好的解决方法。

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

跳转论坛:
Global site tag (gtag.js) - Google Analytics