You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 58 Next »

Routine type conversion in the framework is transparent. Generally, all you need to do is ensure that HTML inputs have names that can be used in OGNL expressions. (HTML inputs are form elements and other GET/POST parameters.)

A Simple Example

Error formatting macro: snippet: java.lang.NullPointerException
Error formatting macro: snippet: java.lang.NullPointerException

The framework ships with a base helper class that simplifies converting to and from Strings, org.apache.struts2.util.StrutsTypeConverter. The helper class makes it easy to write type converters that handle converting objects to Strings as well as from Strings.

From the JavaDocs:

Error formatting macro: snippet: java.lang.NullPointerException

Built in Type Conversion Support

Error formatting macro: snippet: java.lang.NullPointerException

Relationship to Parameter Names

The best way to take advantage of the framework's type conversion is to utilize complete objects (ideally your domain objects directly). There is no need to capture form values using intermediate Strings and primitives and then convert those values to full objects in an Action method.

Here are some tips for leveraging the framework's type conversion capabilties:

  • Use complex OGNL expressions - the framework will automatically take care of creating the actual objects for you.
  • Use JavaBeans! The framework can only create objects if the objects obey the JavaBean specification and provide no-arg constructions, as well as getters and setters where appropriate.
  • Remember that person.name will call getPerson().setName(), but if in order for the framework to create the Person object for you, a setPerson must also exist.
  • For lists and maps, use index notation, such as people[0].name or friends['patrick'].name. Often these HTML form elements are being rendered inside a loop. For JSP Tags, use the iterator tag's status attribute. For FreeMarker Tags, use the special property ${foo_index}[].

  • For multiple select boxes, it isn't possible to use index notation to name each individual item. Instead, name your element people.name and the framework will understand that it should create a new Person object for each selected item and set its name accordingly.

Creating a Type Converter

To create a type converter one would need to extends StrutsTypeConverter.

 public class MyConverter extends StrutsTypeConverter {
    public Object convertFromString(Map context, String[] values, Class toClass) {
       .....
    }

    public String convertToString(Map context, Object o) {
       .....
    }
 }

To allow Struts to recognize that a conversion error has occurred, the converter class need to throw XWorkException or preferably TypeConversionException.

Advanced Type Conversion

The framework also handles advanced type conversion cases, like null property handling and converting values in Maps and Collections, and type conversion error handling.

Null Property Handling

Null property handling will automatically create objects where null references are found.

Error formatting macro: snippet: java.lang.NullPointerException
Error formatting macro: snippet: java.lang.NullPointerException

Collection and Map Support

Collection and Map support provides intelligent null handling and type conversion for Java Collections.

The framework supports ways to discover the object type for elements in a collection. The discover is made via an ObjectTypeDeterminer. A default implementation is provided with the framework. The Javadocs explain how Map and Collection support is discovered in the DefaultObjectTypeDeterminer.

Error formatting macro: snippet: java.lang.NullPointerException

Additionally, you can create your own custom ObjectTypeDeterminer by implementing the ObjectTypeDeterminer interface. There is also an optional ObjectTypeDeterminer that utilizes Java 5 generics. See the Annotations page for more information.

Indexing a collection by a property of that collection

It is also possible to obtain a unique element of a collection by passing the value of a given property of that element. By default, 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.

For an example, see the following two classes:

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

To enable type conversion, put the instruction KeyProperty_fooCollection=id in the MyAction-conversion.properties file. This technique allows use of the idiom fooCollection(someIdValue) to obtain the Foo object with value someIdValue in the Set fooCollection. For example, fooCollection(22) would return the Foo object in the fooCollection Collection whose id property value was 22.

This technique is useful, because it ties a collection element directly to its unique identifier. You are not forced to use an index. You can edit the elements of a collection associated to a bean without any additional coding. For example, parameter name fooCollection(22).name and value Phil would set name the Foo Object in the fooCollection Collection whose id property value was 22 to be Phil.

The framework automatically converts the type of the parameter sent in to the type of the key property using type conversion.

Unlike Map and List element properties, if fooCollection(22) does not exist, it will not be created. If you would like it created, use the notation fooCollection.makeNew[index] where index is an integer 0, 1, and so on. Thus, parameter value pairs fooCollection.makeNew[0]=Phil and fooCollection.makeNew[1]=John would add two new Foo Objects to fooCollection -- one with name property value Phil and the other with name property value Bar. However, in the case of a Set, the equals and hashCode methods should be defined such that they don't only include the id property. Otherwise, one element of the null id properties Foos to be removed from the Set.

An advanced example for indexed Lists and Maps

Here is the model bean used within the list. The KeyProperty for this bean is the id attribute.

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 + '\'' +
                '}';
    }
}

The Action has a beanList attribute initialized with an empty ArrayList.

MyBeanAction.java
public 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;
    }
}

These conversion.properties tell the TypeConverter to use MyBean instances as elements of the List.

MyBeanAction-conversion.properties
KeyProperty_beanList=id
Element_beanList=MyBean
CreateIfNull_beanList=true
  • When submitting this via a form, the id value is used as KeyProperty for the MyBean instances in the beanList.
  • Notice the () notation! Do not use [] notation, which is for Maps only!

  • The value for name will be set to the MyBean instance with this special id.
  • The List does not have null values added for unavailable id values. This approach avoids the risk of OutOfMemoryErrors!
MyBeanAction.jsp
<s:iterator value="beanList" id="bean">
  <stextfield name="beanList(%{bean.id}).name" />
</s:iterator>

Type Conversion Error Handling

Type conversion error handling provides a simple way to distinguish between an input validation problem and an input type conversion problem.

Error formatting macro: snippet: java.lang.NullPointerException

There are two ways the error reporting can occur:

  1. Globally, using the Conversion Error Interceptor
  2. On a per-field basis, using the conversion validator

By default, the conversion interceptor is included in struts-default.xml in the default stack. To keep conversion errors from reporting globally, change the interceptor stack, and add additional validation rules.

Next: Interceptors

  • No labels