Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
161 views
in Technique[技术] by (71.8m points)

java - Binding a map of lists in Spring MVC

I am not sure if this is a complex problem but as a starting person this seems a bit complex to me. I have an object based on which i need to show some values on the UI and let user select some of them, i need to send data back to another controller when user click on the submit button.Here is the structure of my data object

public class PrsData{
private Map<String, List<PrsCDData>> prsCDData;
}

public class PrsCDData{
  private Map<String, Collection<ConfiguredDesignData>> configuredDesignData;
}

public ConfiguredDesignData{
  // simple fields
}

I have set the object in model before showing the view like

model.addAttribute("prsData", productData.getPrData());

In the form i have following settings

<form:form method="post" commandName="prsData" action="${addProductToCartAction}" >
<form:hidden path="prsCDData['${prsCDDataMap.key}']
  [${status.index}].configuredDesignData['${configuredDesignDataMap.key}']
  [${configuredDesignDataStatus.index}].code"/>

<form:hidden path="prsCDData['${prsCDDataMap.key}']
  [${status.index}].configuredDesignData['${configuredDesignDataMap.key}']
  [${configuredDesignDataStatus.index}].description"/>

</form:form>

This is what i have at AddProductToCartController

public String addToCart(@RequestParam("productCodePost") final String code,
@ModelAttribute("prsData") final PrsData prsData, final Model model,
@RequestParam(value = "qty", required = false, defaultValue = "1") final long qty)

On submitting the form i am getting following exception

org.springframework.beans.NullValueInNestedPathException: Invalid property 'prsCDData[Forced][0]' 
of bean class [com.product.data.PrsData]: 
Cannot access indexed value of property referenced in indexed property path 'prsCDData[Forced][0]': returned null

It seems like its trying to access the values on this controller while i am trying to send value to that controller and trying to create same object with selected values

Can any one tell me where i am doing wrong and what i need to take care of

Edit

I did some more research and came to know that Spring do not support auto-populating list/map for custom objects and based on the answer i tried to change implementation like

public class PrsData{
    private Map<String, List<PrsCDData>> prsCDData;
    // lazy init
    public PrsData()
    {
           this.prsCDData = MapUtils.lazyMap(new HashMap<String, List<PrsCDData>>(),
                FactoryUtils.instantiateFactory(PrsCDData.class));
        }
    }

    public class PrsCDData{
      private Map<String, Collection<ConfiguredDesignData>> configuredDesignData;
      public PrsCDData()
    {

       this.configuredDesignData = MapUtils.lazyMap(new HashMap<String,  
                                      List<ConfiguredDesignData>>(),
            FactoryUtils.instantiateFactory(ConfiguredDesignData.class));

    }
    }

but i am getting following exception

org.springframework.beans.InvalidPropertyException: 
Invalid property 'prsCDData[Forced][0]' of bean class [com.data.PrsData]:
Property referenced in indexed property path 'prsCDData[Forced][0]' 
is neither an array nor a List nor a Set nor a Map; 
returned value was [com.data.PrsCDData@6043a24d]

i am not sure which thing i am doing wrong, it seems that either my JSTL expression is not right

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Explanation : if in you controller you have @ModelAttribute("user") User user, and you load a corresponding page that contains <form:form commandName="user">, an empty User is instantiated.

All its attributes are null, or empty in case of a List or a Map. Moreover, its empty lists/maps have been instantiated with an autogrowing implementation. What does it mean ? Let's say we have an empty autogrowing List<Coconut> coconuts. If I do coconuts.get(someIndex).setDiameter(50), it will work instead of throwing an Exception, because the list auto grows and instantiate a coconut for the given index.
Thanks to this autogrowing, submitting a form with the below input will work like a charm :

<form:input path="coconuts[${someIndex}].diameter" />

Now back to your problem : Spring MVC autogrowing works quite well with a chain of objects, each containing a map/list (see this post). But given your exception, it looks like Spring doesn't autogrow the possible objects contained by the autogrowed list/map. In Map<String, List<PrsCDData>> prsCDData, the List<PrsCDData> is a mere empty list with no autogrowing, thus leading to your Exception.

So the solution must use some kind of Apache Common's LazyList or Spring's AutoPopulatingList.
You must implement your own autogrowing map that instantiates a LazyList/AutoPopulatingList for a given index. Do it from scratch or using some kind of Apache Common's LazyMap / MapUtils.lazyMap implementation (so far I have not found a Spring equivalent for LazyMap).

Example of solution, using Apache Commons Collections :

public class PrsData {

  private Map<String, List<PrsCDData>> prsCDData;

  public PrsData() {
      this.prsCDData = MapUtils.lazyMap(new HashMap<String,List<Object>>(), new Factory() {

          public Object create() {
              return LazyList.decorate(new ArrayList<PrsCDData>(), 
                             FactoryUtils.instantiateFactory(PrsCDData.class));
          }

      });
  }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...