Forever A Noob
Forever A Noob

Reputation: 53

Binding comma-separated strings to domain objects using Spring PropertyEditors

I am trying to bind a text field (a comma-separated list of Tag names, Tag being my domain object) to a variable which is a List of Tag objects, in another domain object called Expense. So, a user enters a list of tags for an expense item in the form and this gets bound to a Collection of Tag items in the Expense domain object. So far, what I have done in my code is this:

my jsp file has:

<tr>
  <td>Tags</td>
  <td><form:input path="tags" type="text" name="tags" id="tags_formfield" /></td>
</tr>

my domain object has:

    //bi-directional many-to-many association to Tag
@ManyToMany()
@JoinTable(name="EXPENSES_X_TAGS",
joinColumns= @JoinColumn(name="EXPENSE_ID", referencedColumnName="ID"), 
inverseJoinColumns= @JoinColumn(name="TAG_ID", referencedColumnName="ID"))

private List<Tag> tags;

my controller has:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(List.class, new TagsEditor());
}

I have also tried:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Tag.class, new TagsEditor());
}

And my property editor is:
package com.transience.sandbox.converters; import java.beans.PropertyEditorSupport; import java.util.ArrayList; import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.transience.sandbox.domain.Tag;
import com.transience.sandbox.services.ITagService;

public class TagsEditor extends PropertyEditorSupport {

    @Autowired
    ITagService tagService; 
    protected final Log logger = LogFactory.getLog(getClass());

    @Override
    // Converts a comma separated String of tagNames to a List<Tag> (when submitting form)
    public void setAsText(String stringOfTagNames) {
        List<Tag> tags = null;

        String[] tagNames = stringOfTagNames.split(",");
        logger.info("Looping through tagNames now...");
        for(String tagName : tagNames) {
            logger.info("tag name: " + tagName);
            Tag tag = tagService.findByTagName(tagName);
            tags.add(tag);
        }       
        logger.info("Trying to setValue now...");
        this.setValue(tags);
        logger.info("Value of List of Tags set successfully...");
    }




}

But I am getting the following exception in form submission:

Error 500--Internal Server Error

    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object 'expense' on field 'tags': rejected value [booze,lunch]; codes [methodInvocation.expense.tags,methodInvocation.tags,methodInvocation.java.util.List,methodInvocation]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [expense.tags,tags]; arguments []; default message [tags]]; default message [Property 'tags' threw exception; nested exception is java.lang.NullPointerException]
        at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:110)
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:74)
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:155)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)

I am pretty sure I am doing something fundamentally incorrect, but what am I doing wrong? I am aware of the Converters approach, but I am curious to know how this can be done using PropertyEditors

Thanks, Sanjay

Upvotes: 1

Views: 2131

Answers (1)

pkopac
pkopac

Reputation: 1035

I solved it by adding valueOf(String) to the domain object (also toString is important for backward conversion). Somehow Spring is already able to convert csv to List. Found answer here: How to bind a comma separated string to a Java collection (List) in Spring form?

Upvotes: 2

Related Questions