blob: 34b427fd291777bea63650f6edcaf812308412c6 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="Date-Revision-yyyymmdd" content="20140918"/>
<meta http-equiv="Content-Language" content="en"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Type Conversion</title>
<link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,400italic,600italic,700italic" rel="stylesheet" type="text/css">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/css/main.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet">
<link href="/highlighter/github-theme.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="/bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="/js/community.js"></script>
</head>
<body>
<a href="http://github.com/apache/struts" class="github-ribbon">
<img style="position: absolute; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub">
</a>
<header>
<nav>
<div role="navigation" class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" data-toggle="collapse" data-target="#struts-menu" class="navbar-toggle">
Menu
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/index.html" class="navbar-brand logo"><img src="/img/struts-logo.svg"></a>
</div>
<div id="struts-menu" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Home<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/index.html">Welcome</a></li>
<li><a href="/download.cgi">Download</a></li>
<li><a href="/releases.html">Releases</a></li>
<li><a href="/announce.html">Announcements</a></li>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li><a href="https://www.apache.org/foundation/thanks.html">Thanks!</a></li>
<li><a href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Support<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/mail.html">User Mailing List</a></li>
<li><a href="https://issues.apache.org/jira/browse/WW">Issue Tracker</a></li>
<li><a href="/security.html">Reporting Security Issues</a></li>
<li class="divider"></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Migration+Guide">Version Notes</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Security+Bulletins">Security Bulletins</a></li>
<li class="divider"></li>
<li><a href="/maven/project-info.html">Maven Project Info</a></li>
<li><a href="/maven/struts2-core/dependencies.html">Struts Core Dependencies</a></li>
<li><a href="/maven/struts2-plugins/modules.html">Plugin Dependencies</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Documentation<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/birdseye.html">Birds Eye</a></li>
<li><a href="/primer.html">Key Technologies</a></li>
<li><a href="/kickstart.html">Kickstart FAQ</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Home">Wiki</a></li>
<li class="divider"></li>
<li><a href="/getting-started/">Getting Started</a></li>
<li><a href="/security/">Security Guide</a></li>
<li><a href="/core-developers/">Core Developers Guide</a></li>
<li><a href="/tag-developers/">Tag Developers Guide</a></li>
<li><a href="/maven-archetypes/">Maven Archetypes</a></li>
<li><a href="/plugins/">Plugins</a></li>
<li><a href="/maven/struts2-core/apidocs/index.html">Struts Core API</a></li>
<li><a href="/tag-developers/tag-reference.html">Tag reference</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/FAQs">FAQs</a></li>
<li><a href="http://cwiki.apache.org/S2PLUGINS/home.html">Plugin registry</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Contributing<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/youatstruts.html">You at Struts</a></li>
<li><a href="/helping.html">How to Help FAQ</a></li>
<li><a href="/dev-mail.html">Development Lists</a></li>
<li><a href="/contributors/">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/submitting-patches.html">Submitting patches</a></li>
<li><a href="/builds.html">Source Code and Builds</a></li>
<li><a href="/coding-standards.html">Coding standards</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Contributors+Guide">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/release-guidelines.html">Release Guidelines</a></li>
<li><a href="/bylaws.html">PMC Charter</a></li>
<li><a href="/volunteers.html">Volunteers</a></li>
<li><a href="https://gitbox.apache.org/repos/asf?p=struts.git">Source Repository</a></li>
<li><a href="/updating-website.html">Updating the website</a></li>
</ul>
</li>
<li class="apache"><a href="http://www.apache.org/"><img src="/img/apache.png"></a></li>
</ul>
</div>
</div>
</div>
</nav>
</header>
<article class="container">
<section class="col-md-12">
<a href="index.html" title="back to Core Developers Guide"><< back to Core Developers Guide</a>
<a class="edit-on-gh" href="https://github.com/apache/struts-site/edit/master/source/core-developers/type-conversion.md" title="Edit this page on GitHub">Edit on GitHub</a>
<h1 class="no_toc" id="type-conversion">Type Conversion</h1>
<ul id="markdown-toc">
<li><a href="#built-in-type-conversion-support" id="markdown-toc-built-in-type-conversion-support">Built in Type Conversion Support</a></li>
<li><a href="#relationship-to-parameter-names" id="markdown-toc-relationship-to-parameter-names">Relationship to Parameter Names</a></li>
<li><a href="#creating-a-type-converter" id="markdown-toc-creating-a-type-converter">Creating a Type Converter</a></li>
<li><a href="#applying-a-type-converter-to-an-action" id="markdown-toc-applying-a-type-converter-to-an-action">Applying a Type Converter to an Action</a></li>
<li><a href="#applying-a-type-converter-to-a-bean-or-model" id="markdown-toc-applying-a-type-converter-to-a-bean-or-model">Applying a Type Converter to a bean or model</a></li>
<li><a href="#applying-a-type-converter-for-an-application" id="markdown-toc-applying-a-type-converter-for-an-application">Applying a Type Converter for an application</a></li>
<li><a href="#a-simple-example" id="markdown-toc-a-simple-example">A Simple Example</a></li>
<li><a href="#advanced-type-conversion" id="markdown-toc-advanced-type-conversion">Advanced Type Conversion</a> <ul>
<li><a href="#null-property-handling" id="markdown-toc-null-property-handling">Null Property Handling</a></li>
</ul>
</li>
<li><a href="#collection-and-map-support" id="markdown-toc-collection-and-map-support">Collection and Map Support</a> <ul>
<li><a href="#indexing-a-collection-by-a-property-of-that-collection" id="markdown-toc-indexing-a-collection-by-a-property-of-that-collection">Indexing a collection by a property of that collection</a></li>
<li><a href="#an-advanced-example-for-indexed-lists-and-maps" id="markdown-toc-an-advanced-example-for-indexed-lists-and-maps">An advanced example for indexed Lists and Maps</a></li>
<li><a href="#auto-growth-collection-limit" id="markdown-toc-auto-growth-collection-limit">Auto growth collection limit</a></li>
</ul>
</li>
<li><a href="#type-conversion-error-handling" id="markdown-toc-type-conversion-error-handling">Type Conversion Error Handling</a></li>
<li><a href="#common-problems" id="markdown-toc-common-problems">Common Problems</a> <ul>
<li><a href="#null-and-blank-values" id="markdown-toc-null-and-blank-values">Null and Blank Values</a></li>
<li><a href="#interfaces" id="markdown-toc-interfaces">Interfaces</a></li>
<li><a href="#generics-and-erasure" id="markdown-toc-generics-and-erasure">Generics and Erasure</a></li>
</ul>
</li>
</ul>
<p>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 <a href="ognl.html">OGNL</a> expressions. (HTML inputs are form elements and other GET/POST parameters.)</p>
<h2 id="built-in-type-conversion-support">Built in Type Conversion Support</h2>
<p>Type Conversion is implemented by XWork. XWork will automatically handle the most common type conversion for you.
This includes support for converting to and from Strings for each of the following:</p>
<ul>
<li>String</li>
<li>boolean / Boolean</li>
<li>char / Character</li>
<li>int / Integer, float / Float, long / Long, double / Double</li>
<li>dates - uses the SHORT format for the Locale associated with the current request</li>
<li>arrays - assuming the individual strings can be coverted to the individual items</li>
<li>collections - if not object type can be determined, it is assumed to be a String and a new ArrayList is created
<blockquote>
<p>Note that with arrays the type conversion will defer to the type of the array elements and try to convert each item
individually. As with any other type conversion, if the conversion can’t be performed the standard type conversion
error reporting is used to indicate a problem occurred while processing the type conversion.</p>
</blockquote>
</li>
<li>Enumerations</li>
<li>BigDecimal and BigInteger</li>
</ul>
<h2 id="relationship-to-parameter-names">Relationship to Parameter Names</h2>
<p>There is no need to capture form values using intermediate Strings and primitives. Instead, the framework can read from
and write to properties of objects addressed via OGNL expressions and perform the appropriate type conversion for you.</p>
<p>Here are some tips for leveraging the framework’s type conversion capabilities:</p>
<ul>
<li>Use OGNL expressions - the framework will automatically take care of creating the actual objects for you.</li>
<li>Use JavaBeans - The framework can only create objects that obey the JavaBean specification, provide no-arg constructions
and include getters and setters where appropriate.
<blockquote>
<p>Remember that <code class="highlighter-rouge">person.name</code> will call <code class="highlighter-rouge">getPerson().setName()</code>. If the framework creates the Person object for you,
it remember that a <code class="highlighter-rouge">setPerson</code> method must also exist.</p>
</blockquote>
</li>
<li>The framework will not instantiate an object if an instance already exists. The <code class="highlighter-rouge">PrepareInterceptor</code> or action’s
constructor can be used to create target objects before type conversion.</li>
<li>For lists and maps, use index notation, such as <code class="highlighter-rouge">people[0].name</code> or <code class="highlighter-rouge">friends['patrick'].name</code>. Often these HTML form
elements are being rendered inside a loop. For <a href="../tags-guide/">JSP Tags</a>, use the iterator tag’s status attribute.
For <a href="freemarker-support.html">FreeMarker Tags</a>, use the special property <code class="highlighter-rouge">${foo_index}[]</code>.</li>
<li>For multiple select boxes, it isn’t possible to use index notation to name each individual item. Instead, name your
element <code class="highlighter-rouge">people.name</code> and the framework will understand that it should create a new Person object for each selected
item and set its name accordingly.</li>
</ul>
<h2 id="creating-a-type-converter">Creating a Type Converter</h2>
<p>Create a type converter by extending <code class="highlighter-rouge">StrutsTypeConverter</code>. The Converter’s role is to convert a String to an Object
and an Object to a String.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyConverter</span> <span class="kd">extends</span> <span class="n">StrutsTypeConverter</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">Object</span> <span class="nf">convertFromString</span><span class="o">(</span><span class="n">Map</span> <span class="n">context</span><span class="o">,</span> <span class="n">String</span><span class="o">[]</span> <span class="n">values</span><span class="o">,</span> <span class="n">Class</span> <span class="n">toClass</span><span class="o">)</span> <span class="o">{</span>
<span class="o">.....</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">convertToString</span><span class="o">(</span><span class="n">Map</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">o</span><span class="o">)</span> <span class="o">{</span>
<span class="o">.....</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<blockquote>
<p>To allow Struts to recognize that a conversion error has occurred, the converter class needs to throw XWorkException
or preferably TypeConversionException.</p>
</blockquote>
<h2 id="applying-a-type-converter-to-an-action">Applying a Type Converter to an Action</h2>
<p>Create a file called <code class="highlighter-rouge">ActionClassName-conversion.properties</code> in the same location of the classpath as the Action class
itself resides. Eg. if the action class name is MyAction, the action-level conversion properties file should be
named <code class="highlighter-rouge">MyAction-conversion.properties</code>. If the action’s package is <code class="highlighter-rouge">com.myapp.actions</code> the conversion file should
also be in the classpath at <code class="highlighter-rouge">/com/myapp/actions/</code>.</p>
<p>Within the conversion file, name the action’s property and the Converter to apply to it:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code># syntax: &lt;propertyName&gt; = &lt;converterClassName&gt;
point = com.acme.PointConverter
person.phoneNumber = com.acme.PhoneNumberConverter
</code></pre></div></div>
<p>Type conversion can also be specified via <a href="annotations.html">Annotations</a> within the action.</p>
<h2 id="applying-a-type-converter-to-a-bean-or-model">Applying a Type Converter to a bean or model</h2>
<p>When getting or setting the property of a bean, the framework will look for <code class="highlighter-rouge">classname-conversion.properties</code> in
the same location of the <strong>classpath</strong> as the target bean. This is the same mechanism as used for actions.</p>
<p><strong>Example:</strong>
A custom converter is required for the Amount property of a Measurement bean. The Measurement class cannot be modified
as its located within one of the application’s dependencies. The action using Measurement implements
<code class="highlighter-rouge">ModelDriven&lt;Measurement&gt;</code> so it cannot apply converters to the properties directly.</p>
<p><strong>Solution:</strong>
The conversion file needs to be in the same location of the classpath as Measurement. Create a directory in your source
or resources tree matching the package of Measurement and place the converters file there.
eg. for <code class="highlighter-rouge">com.acme.measurements.Measurement</code>, create a file in the application source/resources
at <code class="highlighter-rouge">/com/acme/measurements/Measurement-conversion.properties</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code># syntax: &lt;propertyName&gt;=&lt;converterClassName&gt;
amount=com.acme.converters.MyCustomBigDecimalConverter
</code></pre></div></div>
<h2 id="applying-a-type-converter-for-an-application">Applying a Type Converter for an application</h2>
<p>Application-wide converters can be specified in a file called <code class="highlighter-rouge">xwork-conversion.properties</code> located in the root of the classpath.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code># syntax: &lt;type&gt; = &lt;converterClassName&gt;
java.math.BigDecimal = com.acme.MyBigDecimalConverter
</code></pre></div></div>
<h2 id="a-simple-example">A Simple Example</h2>
<p>Type conversion is great for situations where you need to turn a String in to a more complex object. Because the web
is type-agnostic (everything is a string in HTTP), Struts 2’s type conversion features are very useful. For instance,
if you were prompting a user to enter in coordinates in the form of a string (such as “3, 22”), you could have
Struts 2 do the conversion both from String to Point and from Point to String.</p>
<p>Using this “point” example, if your action (or another compound object in which you are setting properties on)
has a corresponding ClassName-conversion.properties file, Struts 2 will use the configured type converters for
conversion to and from strings. So turning “3, 22” in to new Point(3, 22) is done by merely adding the following
entry to <b>ClassName-conversion.properties</b> (Note that the PointConverter should impl the TypeConverter
interface):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>point = com.acme.PointConverter
</code></pre></div></div>
<p>Your type converter should be sure to check what class type it is being requested to convert. Because it is used
for both to and from strings, you will need to split the conversion method in to two parts: one that turns Strings in
to Points, and one that turns Points in to Strings.</p>
<p>After this is done, you can now reference your point (using &lt;s:property value=”point”/&gt; in JSP or ${point}
in FreeMarker) and it will be printed as “3, 22” again. As such, if you submit this back to an action, it will be
converted back to a Point once again.</p>
<p>In some situations you may wish to apply a type converter globally. This can be done by editing the file
<code class="highlighter-rouge">xwork-conversion.properties</code> in the root of your class path (typically WEB-INF/classes) and providing a
property in the form of the class name of the object you wish to convert on the left hand side and the class name of
the type converter on the right hand side. For example, providing a type converter for all Point objects would mean
adding the following entry:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com.acme.Point = com.acme.PointConverter
</code></pre></div></div>
<p>Type conversion should not be used as a substitute for i18n. It is not recommended to use this feature to print out
properly formatted dates. Rather, you should use the i18n features of Struts 2 (and consult the JavaDocs for JDK’s
MessageFormat object) to see how a properly formatted date should be displayed.</p>
<p>The framework ships with a base helper class that simplifies converting to and from Strings,
<code class="highlighter-rouge">org.apache.struts2.util.StrutsTypeConverter</code>. The helper class makes it easy to write type converters that handle
converting objects to Strings as well as from Strings.</p>
<p>Base class for type converters used in Struts. This class provides two abstract methods that are used to convert
both to and from strings – the critical functionality that is core to Struts’s type conversion system.</p>
<p>Type converters do not have to use this class. It is merely a helper base class, although it is recommended that
you use this class as it provides the common type conversion contract required for all web-based type conversion.</p>
<p>There’s a hook (fall back method) called <code class="highlighter-rouge">performFallbackConversion</code> of which could be used to perform some fallback
conversion if <code class="highlighter-rouge">convertValue</code> method of this failed. By default it just ask its super class (Ognl’s <code class="highlighter-rouge">DefaultTypeConverter</code>)
to do the conversion.</p>
<p>To allow the framework to recognize that a conversion error has occurred, throw an XWorkException or preferable a TypeConversionException.</p>
<h2 id="advanced-type-conversion">Advanced Type Conversion</h2>
<p>The framework also handles advanced type conversion cases, like null property handling and converting values in Maps
and Collections, and type conversion error handling.</p>
<h3 id="null-property-handling">Null Property Handling</h3>
<p>Null property handling will automatically create objects where null references are found.</p>
<p>Provided that the key <code class="highlighter-rouge">ReflectionContextState#CREATE_NULL_OBJECTS</code> is in the action context with a value of true
(this key is set only during the execution of the <code class="highlighter-rouge">com.opensymphony.xwork2.interceptor.ParametersInterceptor</code>),
OGNL expressions that have caused a <code class="highlighter-rouge">NullPointerException</code> will be temporarily stopped for evaluation while the system
automatically tries to solve the null references by automatically creating the object.</p>
<p>The following rules are used when handling null references:</p>
<ul>
<li>If the property is declared <em>exactly</em> as a <code class="highlighter-rouge">Collection</code> or <code class="highlighter-rouge">List</code>, then an <code class="highlighter-rouge">ArrayList</code> shall be returned and assigned
to the null references</li>
<li>If the property is declared as a <code class="highlighter-rouge">Map</code>, then a <code class="highlighter-rouge">HashMap</code> will be returned and assigned to the null references</li>
<li>If the null property is a simple bean with a no-arg constructor, it will simply be created using
the <code class="highlighter-rouge">ObjectFactory#buildBean(java.lang.Class, java.util.Map)</code> method</li>
</ul>
<p>For example, if a form element has a text field named <code class="highlighter-rouge">person.name</code> and the expression <code class="highlighter-rouge">person</code> evaluates to null, then
this class will be invoked. Because the <code class="highlighter-rouge">person</code> expression evaluates to a <code class="highlighter-rouge">Person</code> class, a new Person is created
and assigned to the null reference. Finally, the name is set on that object and the overall effect is that the system
automatically created a <code class="highlighter-rouge">Person</code> object for you, set it by calling <code class="highlighter-rouge">setUsers()</code> and then finally called
<code class="highlighter-rouge">getUsers().setName()</code> as you would typically expect.</p>
<h2 id="collection-and-map-support">Collection and Map Support</h2>
<p>Collection and Map support provides intelligent null handling and type conversion for Java Collections.</p>
<p>The framework supports ways to discover the object type for elements in a collection. The discover is made via an
<code class="highlighter-rouge">ObjectTypeDeterminer</code>. A default implementation is provided with the framework. The Javadocs explain how <code class="highlighter-rouge">Map</code>
and <code class="highlighter-rouge">Collection</code> support is discovered in the <code class="highlighter-rouge">DefaultObjectTypeDeterminer</code>.</p>
<p>The <code class="highlighter-rouge">ObjectTypeDeterminer</code> looks at the <code class="highlighter-rouge">Class-conversion.properties</code> for entries that indicated what objects are
contained within Maps and Collections. For Collections, such as Lists, the element is specified using the pattern
<code class="highlighter-rouge">Element_xxx</code>, where <code class="highlighter-rouge">xxx</code> is the field name of the collection property in your action or object. For Maps, both the key
and the value may be specified by using the pattern <code class="highlighter-rouge">Key_xxx</code> and <code class="highlighter-rouge">Element_xxx</code>, respectively.</p>
<p>From WebWork 2.1.x, the <code class="highlighter-rouge">Collection_xxx</code> format is still supported and honored, although it is deprecated and will be
removed eventually.</p>
<p>Additionally, you can create your own custom <code class="highlighter-rouge">ObjectTypeDeterminer</code> by implementing the <code class="highlighter-rouge">ObjectTypeDeterminer</code> interface.
There is also an optional <code class="highlighter-rouge">ObjectTypeDeterminer</code> that utilizes Java 5 generics. See the <a href="annotations.html">Annotations</a>
page for more information.</p>
<h3 id="indexing-a-collection-by-a-property-of-that-collection">Indexing a collection by a property of that collection</h3>
<p>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 <code class="highlighter-rouge">&lt;Class&gt;-conversion.properties</code> using
<code class="highlighter-rouge">KeyProperty_xxx=yyy</code>, where xxx is the property of the bean <code class="highlighter-rouge">Class</code> that returns the collection and yyy is the property
of the collection element that we want to index on.</p>
<p>For an example, see the following two classes:</p>
<p><strong>MyAction.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
* @return a Collection of Foo objects
*/</span>
<span class="kd">public</span> <span class="n">Collection</span> <span class="nf">getFooCollection</span><span class="o">()</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">foo</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>Foo.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
* @return a unique identifier
*/</span>
<span class="kd">public</span> <span class="n">Long</span> <span class="nf">getId</span><span class="o">()</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>To enable type conversion, put the instruction <code class="highlighter-rouge">KeyProperty_fooCollection=id</code> in the <code class="highlighter-rouge">MyAction-conversion.properties</code>
file. This technique allows use of the idiom <code class="highlighter-rouge">fooCollection(someIdValue)</code> to obtain the Foo object with value
<code class="highlighter-rouge">someIdValue</code> in the Set <code class="highlighter-rouge">fooCollection</code>. For example, <code class="highlighter-rouge">fooCollection(22)</code> would return the Foo object
in the <code class="highlighter-rouge">fooCollection</code> Collection whose <code class="highlighter-rouge">id</code> property value was 22.</p>
<p>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 <code class="highlighter-rouge">fooCollection(22).name</code> and value <code class="highlighter-rouge">Phil</code> would set name the Foo Object in
the <code class="highlighter-rouge">fooCollection</code> Collection whose <code class="highlighter-rouge">id</code> property value was 22 to be Phil.</p>
<p>The framework automatically converts the type of the parameter sent in to the type of the key property using type conversion.</p>
<p>Unlike Map and List element properties, if <code class="highlighter-rouge">fooCollection(22)</code> does not exist, it will not be created. If you would
like it created, use the notation <code class="highlighter-rouge">fooCollection.makeNew[index]</code> where <code class="highlighter-rouge">index</code> is an integer 0, 1, and so on. Thus,
parameter value pairs <code class="highlighter-rouge">fooCollection.makeNew[0]=Phil</code> and <code class="highlighter-rouge">fooCollection.makeNew[1]=John</code> would add two new Foo Objects
to <code class="highlighter-rouge">fooCollection</code> - one with name property value <code class="highlighter-rouge">Phil</code> and the other with name property value <code class="highlighter-rouge">John</code>. However,
in the case of a Set, the <code class="highlighter-rouge">equals</code> and <code class="highlighter-rouge">hashCode</code> methods should be defined such that they don’t only include the <code class="highlighter-rouge">id</code>
property. Otherwise, one element of the null <code class="highlighter-rouge">id</code> properties Foos to be removed from the Set.</p>
<h3 id="an-advanced-example-for-indexed-lists-and-maps">An advanced example for indexed Lists and Maps</h3>
<p>Here is the model bean used within the list. The KeyProperty for this bean is the <code class="highlighter-rouge">id</code> attribute.</p>
<p><strong>MyBean.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyBean</span> <span class="kd">implements</span> <span class="n">Serializable</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">Long</span> <span class="n">id</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">name</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">Long</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setId</span><span class="o">(</span><span class="n">Long</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setName</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">toString</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"MyBean{"</span> <span class="o">+</span>
<span class="s">"id="</span> <span class="o">+</span> <span class="n">id</span> <span class="o">+</span>
<span class="s">", name='"</span> <span class="o">+</span> <span class="n">name</span> <span class="o">+</span> <span class="sc">'\''</span> <span class="o">+</span>
<span class="sc">'}'</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The Action has a <code class="highlighter-rouge">beanList</code> attribute initialized with an empty <code class="highlighter-rouge">ArrayList</code>.</p>
<p><strong>MyBeanAction.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyBeanAction</span> <span class="kd">implements</span> <span class="n">Action</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">List</span> <span class="n">beanList</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">();</span>
<span class="kd">private</span> <span class="n">Map</span> <span class="n">beanMap</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o">();</span>
<span class="kd">public</span> <span class="n">List</span> <span class="nf">getBeanList</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">beanList</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setBeanList</span><span class="o">(</span><span class="n">List</span> <span class="n">beanList</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">beanList</span> <span class="o">=</span> <span class="n">beanList</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">Map</span> <span class="nf">getBeanMap</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">beanMap</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setBeanMap</span><span class="o">(</span><span class="n">Map</span> <span class="n">beanMap</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">beanMap</span> <span class="o">=</span> <span class="n">beanMap</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>These <code class="highlighter-rouge">conversion.properties</code> tell the TypeConverter to use MyBean instances as elements of the List.</p>
<p><strong>MyBeanAction-conversion.properties</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>KeyProperty_beanList=id
Element_beanList=MyBean
CreateIfNull_beanList=true
</code></pre></div></div>
<ul>
<li>When submitting this via a form, the <code class="highlighter-rouge">id</code> value is used as KeyProperty for the MyBean instances in the beanList.</li>
<li>Notice the <code class="highlighter-rouge">()</code> notation! Do not use <code class="highlighter-rouge">[]</code> notation, which is for Maps only!</li>
<li>The value for name will be set to the MyBean instance with this special id.
0 The List does not have null values added for unavailable id values. This approach avoids the risk of <code class="highlighter-rouge">OutOfMemoryErrors</code>!</li>
</ul>
<p><strong>MyBeanAction.jsp</strong></p>
<pre><code class="language-jsp">&lt;s:iterator value="beanList" id="bean"&gt;
&lt;stextfield name="beanList(%{bean.id}).name" /&gt;
&lt;/s:iterator&gt;
</code></pre>
<h3 id="auto-growth-collection-limit">Auto growth collection limit</h3>
<p>There is a special constant that limits auto growth of a collection, by default it is set to <strong>255</strong> which means
only 256 elements are allowed in the collection. This limitation was introduced to avoid DoS attacks.
If you know that the collection can have more elements you must define the below constant and set its value
to desire size of the collection.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.ognl.autoGrowthCollectionLimit"</span> <span class="na">value=</span><span class="s">"1024"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<blockquote>
<p>NOTE: before this constant was named as <code class="highlighter-rouge">xwork.autoGrowCollectionLimit</code> but it is deprecated now and will be removed
with a next major release.</p>
</blockquote>
<h2 id="type-conversion-error-handling">Type Conversion Error Handling</h2>
<p>Type conversion error handling provides a simple way to distinguish between an input <code class="highlighter-rouge">validation</code> problem
and an input <code class="highlighter-rouge">type conversion</code> problem.</p>
<p>Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the input
“abc” could not be converted to a number might be important. On the other hand, reporting that an empty string “””,
cannot be converted to a number might not be important - especially in a web environment where it is hard to distinguish
between a user not entering a value vs. entering a blank value.</p>
<p>By default, all conversion errors are reported using the generic i18n key <code class="highlighter-rouge">xwork.default.invalid.fieldvalue</code>,
which you can override (the default text is <em>Invalid field value for field “xxx”</em>, where xxx is the field name)
in your global i18n resource bundle.</p>
<p>However, sometimes you may wish to override this message on a per-field basis. You can do this by adding an i18n
key associated with just your action (<code class="highlighter-rouge">Action.properties</code>) using the pattern <code class="highlighter-rouge">invalid.fieldvalue.xxx</code>, where xxx
is the field name.</p>
<p>It is important to know that none of these errors are actually reported directly. Rather, they are added to a map
called <em>conversionErrors</em> in the ActionContext. There are several ways this map can then be accessed and the errors
can be reported accordingly.</p>
<p>There are two ways the error reporting can occur:</p>
<ol>
<li>Globally, using the <a href="conversion-error-interceptor.html">Conversion Error Interceptor</a></li>
<li>On a per-field basis, using the <a href="conversion-validator.html">conversion validator</a></li>
</ol>
<p>By default, the conversion interceptor is included in <a href="struts-default-xml.html">struts-default.xml</a> in the default stack.
To keep conversion errors from reporting globally, change the interceptor stack, and add additional validation rules.</p>
<h2 id="common-problems">Common Problems</h2>
<h3 id="null-and-blank-values">Null and Blank Values</h3>
<p>Some properties cannot be set to null. Primitives like boolean and int cannot be null. If your action needs to or will
accept null or blank values, use the object equivalents Boolean and Integer. Similarly, a blank string “” cannot be set
on a primitive. At the time of writing, a blank string also cannot be set on a BigDecimal or BigInteger. Use server-side
validation to prevent invalid values from being set on your properties (or handle the conversion errors appropriately).</p>
<h3 id="interfaces">Interfaces</h3>
<p>The framework cannot instantiate an object if it can’t determine an appropriate implementation. It recognizes well-known
collection interfaces (List, Set, Map, etc) but cannot instantiate MyCustomInterface when all it sees is the interface.
In this case, instantiate the target implementation first (eg. in a prepare method) or substitute in an implementation.</p>
<h3 id="generics-and-erasure">Generics and Erasure</h3>
<p>The framework will inspect generics to determine the appropriate type for collections and array elements. However, in
some cases Erasure can result in base types that cannot be converted (typically Object or Enum).</p>
<p>The following is an example of this problem:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">Measurement</span><span class="o">&lt;</span><span class="n">T</span> <span class="kd">extends</span> <span class="n">Enum</span><span class="o">&gt;</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUnits</span><span class="o">(</span><span class="n">T</span> <span class="n">enumValue</span><span class="o">)</span> <span class="o">{...}</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Area</span> <span class="kd">extends</span> <span class="n">Measurement</span><span class="o">&lt;</span><span class="n">UnitsOfArea</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUnits</span><span class="o">(</span><span class="n">UnitsOfArea</span> <span class="n">enumValue</span><span class="o">){...}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Although to the developer the area.setUnits(enumValue) method only accepts a UnitsOfArea enumeration, due to erasure
the signature of this method is actually setUnits(java.lang.Enum). The framework does not know that the parameter is
a UnitsOfArea and when it attempts to instantiate the Enum an exception is thrown (java.lang.IllegalArgumentException:
java.lang.Enum is not an enum type).</p>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2018 <a href="http://www.apache.org/">The Apache Software Foundation </a>.
All Rights Reserved.
</div>
<div class="col-md-12">
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation.
</div>
<div class="col-md-12">Logo and website design donated by <a href="https://softwaremill.com/">SoftwareMill</a>.</div>
</footer>
<script>!function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = "//platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");</script>
<script src="https://apis.google.com/js/platform.js" async="async" defer="defer"></script>
<div id="fb-root"></div>
<script>(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_GB/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
</body>
</html>