| <!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-2021.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: <propertyName> = <converterClassName> |
| 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<Measurement></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: <propertyName>=<converterClassName> |
| 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: <type> = <converterClassName> |
| 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 <s:property value=”point”/> 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"><Class>-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"><s:iterator value="beanList" id="bean"> |
| <stextfield name="beanList(%{bean.id}).name" /> |
| </s:iterator> |
| </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"><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">/></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"><</span><span class="n">T</span> <span class="kd">extends</span> <span class="n">Enum</span><span class="o">></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"><</span><span class="n">UnitsOfArea</span><span class="o">></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 © 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> |