JavaScud | Forum | JIRA | Blog |
  Dashboard > WebWork2文档中文化计划 > WebWork > Type Conversion
  WebWork2文档中文化计划 Log In View a printable version of the current page.  
  Type Conversion
Added by scud, last edited by scud on May 19, 2006  (view change)
Labels: 
(None)

在所有的基于Web的Java开发框架中, WebWork拥有最优秀的类型转换能力. 通常情况下, 要利用这种能力, 只需要把HTML输入项(表单元素和其他GET/POST的参数)命名为合法的OGNL表达式.

简单的例子

当你需要将一个字符串转换成为一个更为复杂的对象时, 类型转换能发挥强大的作用. 由于Web是类型不可知的(type-agnostic)(HTTP中任何类型都是字符串), 因此WebWork的类型转换功能非常有用. 例如, 如果你提示用户使用字符串格式("3, 22")输入一个坐标, 你需要让WebWork完成String到Point和Point到String的转换.

使用"point"的例子, 如果活动(或者其他需要设置属性的组合对象)有一个对应的ClassName-conversion.properties文件, WebWork会使用配置的类型转换器进行该类和字符串的转换. 因此将"3, 22"转换成new Point(3, 22)只需在 ClassName-conversion.properties 中加入下列内容(注意, PointConverter需要实现接口ognl.TypeConverter):

point = com.acme.PointConverter

你编写的类型转换器必须检查被用于转换哪种类型. 由于都是与String进行相互转换, 需要将转换方法分为两部分: 一个将String转换为Point, 另一个将Point转换为String.

这些完成后, 你可以引用point对象(在JSP中使用<ww:property value="point"/>或在FreeMarker中使用${point})并且它将输出为"3, 22". 同样的, 如果你将它提交到活动, 它将再次转换成Point.

有时你可能希望将一个类型转换器应用到全局范围. 这可以通过编辑 xwork-conversion.properties (位于根类路径, 通常是WEB-INF/classes目录)并以下列形式添加一个属性定义: 左边是你希望转换的类名称, 右边是转换器的类名称. 例如, 为所有的Point对象提供一个类型转换器意味着增加下列内容:

com.acme.Point = com.acme.PointConverter

(摘自snippet:id=javadoc|javadoc=true|url=com.opensymphony.xwork.util.XWorkConverter)

类型转换不能作为i18n的替代品. 不推荐使用这一特性输出相应的日期格式. 相反, 你应该使用WebWork的i18n功能(并且研究一下JDK中MessageFormat的JavaDoc)来了解如何使用相应正确的日期格式.
(摘自snippet:id=i18n-note|javadoc=true|url=com.opensymphony.xwork.util.XWorkConverter)

WebWork附带了一个帮助基类(a helper base class), 利用它可以很方便的完成类型与字符串之间的相互转换. 这个类就是 com.opensymphony.webwork.util.WebWorkTypeConverter. 该类可以让你很方便的编写处理对象和字符串相互转换的类型转换器. 下面是从该类的JavaDoc中摘录的内容:

本类是WebWork使用的类型装换器的一个基类. 该类提供了两个抽象方法用于对象与字符串的相互转换 - 这是WebWork类型转换系统的关键功能.

类型转换器也可以不使用该类. 它基本上是一个帮助类, 尽管它是推荐使用的, 它为子类提供了所有基于Web的类型转换所需的一般接口约束.

它还包含一个hook(后备方法), 名为performFallbackConversion, 该方法可用于在convertValue方法执行失败时执行一些后备的转换. 缺省情况下, 它使用父类(Ognl的DefaultTypeConverter)的convertValue方法完成转换.

(摘自snippet:id=javadoc|javadoc=true|url=com.opensymphony.webwork.util.WebWorkTypeConverter)

内建的(Built in)类型转换支持

WebWork可以替你自动完成大多数常用的类型转换. 已支持的与字符串之间转换类型包括:

  • String
  • boolean / Boolean
  • char / Character
  • int / Integer, float / Float, long / Long, double / Double
  • dates - 使用当前request指定的Locale信息对应的SHORT格式
  • arrays - 假定每一个字符串都能够转换成对应的数组元素
  • collections - 如果不能确定对象类型, 将假定集合元素类型为String, 并创建一个新的ArrayList

注意, 对于数组的类型转换将按造数组元素的类型来单独转换每一个元素. 而在其他类型转换中, 如果转换无法实现, 将使用标准的类型转换错误报告.

(摘自snippet:id=javadoc|javadoc=true|url=com.opensymphony.xwork.util.XWorkBasicConverter)

参数名称的关系

利用WebWork的类型转换最好的方式是使用已完成的(completes)对象(理想情况下应当直接使用业务对象(domain objects)), 而不是使用基本类型或字符串类型的表单参数值作为中间值, 然后在Action的execute()方法中把这些中间值转换成完整的对象(rather than submitting form values on to intermediate primitives and strings in your action and then converting those values to full objects in the execute() method). 下面是一些提示:

  • 使用组合的(complex)OGNL表达式 - WebWork能自动创建你所需的实际对象.
  • 使用JavaBeans! WebWork只能创建遵守JavaBean规范的对象, 这需要你的对象提供一个无参构造函数, 并包含适当的getter和setter方法.
  • 记住 person.name 将调用 getPerson().setName(), 但如果你希望WebWork创建Person对象, 那么必须包含一个setPerson() 方法.
  • 对于list和map对象, 使用索引符号, 如 people[0].name or friends['patrick'].name. 通常这些HTML表单元素是在一个循环中绘制出来的, 因此你可以在JSP Tags中使用iterator标签的状态属性(status attribute)或在FreeMarker Tags中使用${foo_index} 来指定这一属性(指index value, 译注).
  • 对于多选的列表, 显然不能为每个单独的选项使用对应的属性符号来命名(由于). 替代的方法是, 使用简单的名称 people.name 来命名你的表单元素, WebWork知道需要为每一个选中的选项创建一个新的Person对象并设定它的名字.

高级类型转换

WebWork还有一些非常优秀的(同样易与使用)的类型转换特性. 对空值(Null)属性的处理可以在发现空值引用时自动创建对象. 对Collection和Map的支持提供了针对Java集合的智能空值处理和类型转换. 类型转换错误处理提供了一种简单的方法可以把输入校验问题和输入类型装换问题区别开.

空值属性处理

通过把action context中的键值 CREATE_NULL_OBJECTS 设置为true支持空值处理(该键只在com.opensymphony.xwork.interceptor.ParametersInterceptoris执行过程中进行设置), 这样, 出现NullPointerException异常的OGNL表达式将被自动临时中断, 然后系统将通过创建所需对象的方法来自动尝试解决null引用.(OGNL中包含了NullHandler接口, 允许对空值引用进行转换, WebWork实现了这一接口并替换了OGNL的缺省实现, 译注)

处理控制引用时将遵循下列规则:

  • 如果属性声明为Collection或List, 将返回一个ArrayList并赋值给空引用.
  • 如果属性声明为Map, 将返回一个HashMap并赋值给空引用.
  • 如果空值属性是一个带有无参构造函数的简单Bean, 将使用ObjectFactory.buildBean(java.lang.Class, java.util.Map)方法创建一个实例.

(摘自snippet:id=javadoc|javadoc=true|url=com.opensymphony.xwork.util.InstantiatingNullHandler)

例如, 如果表单中包含一个文本字段名为 person.name 而表达式 person 运算结果为null, 那么该类将被调用. 由于表达式 person 类型为Person, 因此将创建一个新的Person实例并赋值给空值引用. 最后, name值被赋给该实例的name属性. 全部过程是系统自动创建一个Person实例, 并调用setPerson()方法将它赋给空值引用, 最后调用getPerson().setName(), 而这通常是想要的结果.

(摘自snippet:id=example|javadoc=true|url=com.opensymphony.xwork.util.InstantiatingNullHandler)

Collection和Map支持

WebWork支持多种方法来判断集合中的对象类型. 这是通过一个 ObjectTypeDeterminer 完成的. WebWork提供了缺省实现. 下面的JavaDocs解释了对Map和Collection的支持是如何在DefaultObjectTypeDeterminer中完成的:

ObjectTypeDeterminer检查 Class-conversion.properties 文件中包含的用于表示Map和Collection中包含的对象类型的相关内容 . 对于Collection, 如List, 使用格式 Element_xxx 来指定其中的元素类型, 这里xxx是action或其他对象中的集合属性名称. 对于Map, 需要按照格式 Key_xxxElement_xxx 分别指定key和value的类型.

从WebWork 2.1.x开始, 仍然支持Collection_xxx这样的书写格式, 尽管这种格式已经被声明废弃而且最终将被去除.

(摘自snippet:id=javadoc|javadoc=true|url=com.opensymphony.xwork.util.DefaultObjectTypeDeterminer)

除此之外, 也可以实现接口ObjectTypeDeterminer来创建自己的定制ObjectTypeDeterminer. WebWork也包含一个可选的使用Java5范型(generics)技术实现的ObjectTypeDeterminer. 更多信息参见J2SE 5 Support.

使用集合的属性索引集合元素(Indexing a collection by a property of that collection)

也可以向WebWork传递元素的某个给定属性的值来获取集合中的唯一元素(element). 缺省情况下, 这个属性由 Class-conversion.properties中定义的KeyProperty_xxx=yyy决定, 这里的xxx是返回集合的JavaBean类型名称, yyy是我们用于索引集合中元素的属性名称. (the property of the element of the collection is determined in Class-conversion.properties using KeyProperty_xxx=yyy where xxx is the property of the bean 'Class' that returns the collection and yyy is the property of the collection element that we want to index on.) 下面的两个类是一个示例:

MyAction.java
/**
 * @return a Collection of Foo objects
 */
public Collection getFooCollection()
{
    return foo;
}
Foo.java
/**
 * @return a unique identifier
 */
public Long getId()
{
    return id;
}

然后将 KeyProperty_fooCollection=id 放在MyAction-conversion.properties文件中. 这样就可以使用 fooCollection(someIdValue) 从集合fooCollection中获取id等于 someIdValue 的Foo对象. 例如, fooCollection(22) 将得到id值为22的Foo对象.

这一点十分有用, 因为这直接将一个集合中的元素与它的唯一标志符联系起来, 而不需要强制使用索引, 从而允许修改一个Bean的集合中的元素而不需要编写额外的代码. 例如, 值为 Phil 的参数 fooCollection(22).name 将集合fooClooection中id属性值为22的Foo对象的name属性设置为"Phil".

Webwork可以使用类型转换自动将参数的类型转换成key属性的类型.

与Map和List元素的属性不同, 如果fooCollection(22)不存在, WebWork不会创建新的对象. 想要做到这一点, 可以使用符号 fooCollection.makeNew[index], 在这里index是一个整数(0, 1等等). 因此, 参数 fooCollection.makeNew[0]=Phil 以及 fooCollection.makeNew[1]=John 将在fooCollection中添加两个新的Foo对象, 一个name属性值为Phil, 另一个为Bar. 注意, 不管用哪种方法, 在使用Set类型时, 必须定义对象的equals方法和hashCode方法来并保证他们不仅仅包含id属性. 这将导致id属性为null的元素可以从Set中删除.

索引的List和Map的高级示例

下面是一个用于List中的模型bean.
该类的KeyProperty是id属性.

MyBean.java
public class MyBean implements Serializable {

    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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


    public String toString() {
        return "MyBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

下面的action有一个beanList属性, 它被初始化为一个空的ArrayList对象.

MyBeanAction.java
ublic class MyBeanAction implements Action {

    private List beanList = new ArrayList();
    private Map beanMap = new HashMap();

    public List getBeanList() {
        return beanList;
    }

    public void setBeanList(List beanList) {
        this.beanList = beanList;
    }

    public Map getBeanMap() {
        return beanMap;
    }

    public void setBeanMap(Map beanMap) {
        this.beanMap = beanMap;
    }

    public String execute() throws Exception {
        return SUCCESS;
    }
}

定义在conversion.properties中的内容告诉TypeConverter使用MyBean的实例作为List的元素.

MyBeanAction-conversion.properties
KeyProperty_beanList=id
Element_beanList=MyBean
CreateIfNull_beanList=true

当通过表单提交到Action时, (id)的值被当作beanList中MyBean实例的KeyProperty.
注意()符号! 不要使用[], 那仅能用在Map里!
属性的name的值将根据指定的id设置到对应的MyBean实例中.
无效的id值将不会再加入null值.
这就避免了OutOfMemory异常的产生!

MyBeanAction.jsp
<ww:iterator value="beanList" id="bean">
  <ww:textfield name="beanList(%{bean.id}).name" />
</ww:iterator>

类型转换错误处理

当类型转换期间发生错误时, 有时希望报告这些错误, 而有时不希望报告. 例如, 报告输入的"abc"不能转换成数字可能很重要. 另一方面, 报告一个空字符串("")不能装换成数字可能不重要 - 除非是在一个Web环境下, 难以区分用户没有输入值还是输入了一个空白值.

缺省情况下, 所有的装换错误使用通用的i18n信息 xwork.default.invalid.fieldvalue , 你可以在你的全局il8n资源包中替换他(缺省文本是"Invalid field value for field xxx", 这里xxx是字段名称).

无论如何, 有时你会希望能够在每个字段上替换这一信息. 你可以在action相关的资源文件(Action.properties)中添加一个i18n信息: invalid.fieldvalue.xxx, 这里xxx是字段名称.

需要知道的是, 这些错误不会直接报告出来. 他们被添加到ActionContext.conversionErrors中. 有几种方法可以访问该map从而可以报告这些错误.

(摘自snippet:id=error-reporting|javadoc=true|url=com.opensymphony.xwork.util.XWorkConverter)

错误报告可以两种方式处理:

  1. 全局方式, 使用 Conversion Error Interceptor 报告错误
  2. 以每一个字段为基点, 使用 conversion validator 报告错误

缺省情况下, conversion interceptor包含在 webwork-default.xml 的缺省截取器栈中, 如果不希望使用全局错误报告方式, 需要修改截取器栈并添加其他的校验规则(指第二种方式, 在字段上使用conversion validator, 译注).

Site running on a free Atlassian Confluence Open Source Project License granted to WebWork China. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.3 Build:#808 May 29, 2007) - Bug/feature request - Contact Administrators