jquery – 使用JSON将嵌套对象发布到Spring MVC控制器

前端之家收集整理的这篇文章主要介绍了jquery – 使用JSON将嵌套对象发布到Spring MVC控制器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个控制器,POST处理程序定义如下:
  1. @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
  2. public @ResponseBody AjaxResponse saveVendor( @Valid UIVendor vendor,BindingResult result,Locale currentLocale )

当以JSON格式查看时,UIVendor对象如下所示:

  1. var vendor =
  2. {
  3. vendorId: 123,vendorName: "ABC Company",emails : [
  4. { emailAddress: "abc123@abc.com",flags: 2 },{ emailAddress: "xyz@abc.com",flags: 3 }
  5. ]
  6. }

UIVendor bean有一个名为“Emails”的字段,类型为ArrayList,带有适当的setter和getter(getEmails / setEmails)。 NotificationEmail对象也具有适当的公共setter / getter。

当我尝试使用以下代码发布对象时:

  1. $.post("ajax/saveVendor.do",$.param(vendor),saveEntityCallback,"json" );

我在日志中收到此错误

  1. Invalid property 'emails[0][emailAddress]' of bean class [beans.UIVendor]: Property referenced in indexed property path 'emails[0][emailAddress]' is neither an array nor a List nor a Map; returned value was [abc123@abc.com]

如何正确地将这样的嵌套对象发布到Spring控制器并将其正确地反序列化到适当的对象结构中。

UPDATE
根据Per Bohzo的要求,这里是UIVendor类的内容。此类包装Web服务生成的bean类,将VendorAttributes作为单独的字段公开:

  1. package com.mycompany.beans;
  2.  
  3. import java.util.*;
  4. import org.apache.commons.lang.*;
  5. import com.mycompany.domain.Vendor;
  6. import com.mycompany.domain.VendorAttributes;
  7. import org.apache.commons.logging.*;
  8. import org.codehaus.jackson.annotate.JsonIgnore;
  9.  
  10. public class UIVendor
  11. {
  12. private final Log logger = LogFactory.getLog( this.getClass() );
  13. private Vendor vendor;
  14. private boolean ftpFlag;
  15. private String ftpHost;
  16. private String ftpPath;
  17. private String ftpUser;
  18. private String ftpPassword;
  19. private List<UINotificationEmail> emails = null;
  20.  
  21. public UIVendor() { this( new Vendor() ); }
  22. public UIVendor( Vendor vendor )
  23. {
  24. this.vendor = vendor;
  25. loadVendorAttributes();
  26. }
  27.  
  28. private void loadVendorAttributes()
  29. {
  30. this.ftpFlag = false;
  31. this.ftpHost = this.ftpPassword = this.ftpPath = this.ftpUser = "";
  32. this.emails = null;
  33.  
  34. for ( VendorAttributes a : this.vendor.getVendorAttributes() )
  35. {
  36. String key = a.getVendorFakey();
  37. String value = a.getVendorFaValue();
  38. int flags = a.getFlags();
  39.  
  40. if ( StringUtils.isBlank(key) || StringUtils.isBlank(value) ) continue;
  41.  
  42. if ( key.equals( "ftpFlag" ) )
  43. {
  44. this.ftpFlag = BooleanUtils.toBoolean( value );
  45. }
  46. else if ( key.equals( "ftpHost" ) )
  47. {
  48. this.ftpHost = value;
  49. }
  50. else if ( key.equals("ftpPath") )
  51. {
  52. this.ftpPath = value;
  53. }
  54. else if ( key.equals("ftpUser") )
  55. {
  56. this.ftpUser = value;
  57. }
  58. else if ( key.equals("ftpPassword") )
  59. {
  60. this.ftpPassword = value;
  61. }
  62. else if ( key.equals("email") )
  63. {
  64. UINotificationEmail email = new UINotificationEmail(value,flags);
  65. this.getEmails().add( email );
  66. }
  67. }
  68. }
  69.  
  70. private void saveVendorAttributes()
  71. {
  72. int id = this.vendor.getVendorId();
  73. List<VendorAttributes> attrs = this.vendor.getVendorAttributes();
  74. attrs.clear();
  75.  
  76. if ( this.ftpFlag )
  77. {
  78. VendorAttributes flag = new VendorAttributes();
  79. flag.setVendorId( id );
  80. flag.setStatus( "A" );
  81. flag.setVendorFakey( "ftpFlag" );
  82. flag.setVendorFaValue( BooleanUtils.toStringTrueFalse( this.ftpFlag ) );
  83. attrs.add( flag );
  84.  
  85. if ( StringUtils.isNotBlank( this.ftpHost ) )
  86. {
  87. VendorAttributes host = new VendorAttributes();
  88. host.setVendorId( id );
  89. host.setStatus( "A" );
  90. host.setVendorFakey( "ftpHost" );
  91. host.setVendorFaValue( this.ftpHost );
  92. attrs.add( host );
  93.  
  94. if ( StringUtils.isNotBlank( this.ftpPath ) )
  95. {
  96. VendorAttributes path = new VendorAttributes();
  97. path.setVendorId( id );
  98. path.setStatus( "A" );
  99. path.setVendorFakey( "ftpPath" );
  100. path.setVendorFaValue( this.ftpPath );
  101. attrs.add( path );
  102. }
  103.  
  104. if ( StringUtils.isNotBlank( this.ftpUser ) )
  105. {
  106. VendorAttributes user = new VendorAttributes();
  107. user.setVendorId( id );
  108. user.setStatus( "A" );
  109. user.setVendorFakey( "ftpUser" );
  110. user.setVendorFaValue( this.ftpUser );
  111. attrs.add( user );
  112. }
  113.  
  114. if ( StringUtils.isNotBlank( this.ftpPassword ) )
  115. {
  116. VendorAttributes password = new VendorAttributes();
  117. password.setVendorId( id );
  118. password.setStatus( "A" );
  119. password.setVendorFakey( "ftpPassword" );
  120. password.setVendorFaValue( this.ftpPassword );
  121. attrs.add( password );
  122. }
  123. }
  124. }
  125.  
  126. for ( UINotificationEmail e : this.getEmails() )
  127. {
  128. logger.debug("Adding email " + e );
  129. VendorAttributes email = new VendorAttributes();
  130. email.setStatus( "A" );
  131. email.setVendorFakey( "email" );
  132. email.setVendorFaValue( e.getEmailAddress() );
  133. email.setFlags( e.getFlags() );
  134. email.setVendorId( id );
  135. attrs.add( email );
  136. }
  137. }
  138.  
  139. @JsonIgnore
  140. public Vendor getVendor()
  141. {
  142. saveVendorAttributes();
  143. return this.vendor;
  144. }
  145.  
  146. public int getVendorId()
  147. {
  148. return this.vendor.getVendorId();
  149. }
  150. public void setVendorId( int vendorId )
  151. {
  152. this.vendor.setVendorId( vendorId );
  153. }
  154.  
  155. public String getVendorType()
  156. {
  157. return this.vendor.getVendorType();
  158. }
  159. public void setVendorType( String vendorType )
  160. {
  161. this.vendor.setVendorType( vendorType );
  162. }
  163.  
  164. public String getVendorName()
  165. {
  166. return this.vendor.getVendorName();
  167. }
  168. public void setVendorName( String vendorName )
  169. {
  170. this.vendor.setVendorName( vendorName );
  171. }
  172.  
  173. public String getStatus()
  174. {
  175. return this.vendor.getStatus();
  176. }
  177. public void setStatus( String status )
  178. {
  179. this.vendor.setStatus( status );
  180. }
  181.  
  182. public boolean isFtpFlag()
  183. {
  184. return this.ftpFlag;
  185. }
  186. public void setFtpFlag( boolean ftpFlag )
  187. {
  188. this.ftpFlag = ftpFlag;
  189. }
  190.  
  191. public String getFtpHost()
  192. {
  193. return this.ftpHost;
  194. }
  195. public void setFtpHost( String ftpHost )
  196. {
  197. this.ftpHost = ftpHost;
  198. }
  199.  
  200. public String getFtpPath()
  201. {
  202. return this.ftpPath;
  203. }
  204. public void setFtpPath( String ftpPath )
  205. {
  206. this.ftpPath = ftpPath;
  207. }
  208.  
  209. public String getFtpUser()
  210. {
  211. return this.ftpUser;
  212. }
  213. public void setFtpUser( String ftpUser )
  214. {
  215. this.ftpUser = ftpUser;
  216. }
  217.  
  218. public String getFtpPassword()
  219. {
  220. return this.ftpPassword;
  221. }
  222. public void setFtpPassword( String ftpPassword )
  223. {
  224. this.ftpPassword = ftpPassword;
  225. }
  226.  
  227. public List<UINotificationEmail> getEmails()
  228. {
  229. if ( this.emails == null )
  230. {
  231. this.emails = new ArrayList<UINotificationEmail>();
  232. }
  233. return emails;
  234. }
  235.  
  236. public void setEmails(List<UINotificationEmail> emails)
  237. {
  238. this.emails = emails;
  239. }
  240. }

更新2
这是杰克逊的输出

  1. {
  2. "vendorName":"MAIL","vendorId":45,"emails":
  3. [
  4. {
  5. "emailAddress":"dfg","success":false,"failure":false,"flags":0
  6. }
  7. ],"vendorType":"DFG","ftpFlag":true,"ftpHost":"kdsfjng","ftpPath":"dsfg","ftpUser":"sdfg","ftpPassword":"sdfg","status":"A"
  8. }

这是我在POST上返回的对象的结构:

  1. {
  2. "vendorId":"45","vendorName":"MAIL","status":"A","emails":
  3. [
  4. {
  5. "success":"false","failure":"false","emailAddress":"dfg"
  6. },{
  7. "success":"true","failure":"true","emailAddress":"pfc@sj.org"
  8. }
  9. ]
  10. }

我也尝试过使用www.json.org上的JSON库进行序列化,结果就是你在上面看到的。但是,当我发布该数据时,传递给控制器​​的UIVendor对象中的所有字段都为空(尽管该对象不是)。

解决方法

更新:从Spring 3.1开始,可以使用 @Valid On @RequestBody Controller Method Arguments
  1. @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
  2. public @ResponseBody AjaxResponse saveVendor( @Valid @RequestBody UIVendor vendor,Locale currentLocale )

经过多次反复试验,我终于想出了问题所在。使用以下控制器方法签名时:

  1. @RequestMapping(value="/ajax/saveVendor.do",Locale currentLocale )

客户端脚本必须以后数据(通常是“application / x-www-form-urlencoded”)格式传递对象中的字段(即,field = value& field2 = value2)。这是在jQuery中完成的,如下所示:

  1. $.post( "mycontroller.do",$.param(object),callback,"json" )

这适用于没有子对象或集合的简单POJO对象,但是一旦为传递的对象引入了显着的复杂性,jQuery用于序列化对象数据的符号就不会被Spring的映射逻辑识别:

  1. object[0][field]

解决这个问题的方法是将控制器中的方法签名更改为:

  1. @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
  2. public @ResponseBody AjaxResponse saveVendor( @RequestBody UIVendor vendor,Locale currentLocale )

并将呼叫从客户端更改为:

  1. $.ajax(
  2. {
  3. url:"ajax/mycontroller.do",type: "POST",data: JSON.stringify( objecdt ),success: callback,dataType: "json",contentType: "application/json"
  4. } );

这需要使用JSON javascript库。它还强制将contentType设置为“application / json”,这是Spring在使用@RequestBody注释时所期望的,并将对象序列化为Jackson可以反序列化为有效对象结构的格式。

唯一的副作用是现在我必须在控制器方法中处理我自己的对象验证,但这相对简单:

  1. BindingResult result = new BeanPropertyBindingResult( object,"MyObject" );
  2. Validator validator = new MyObjectValidator();
  3. validator.validate( object,result );

如果有人有任何改进这个过程的建议,我会全力以赴。

猜你在找的jQuery相关文章