- @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
- public @ResponseBody AjaxResponse saveVendor( @Valid UIVendor vendor,BindingResult result,Locale currentLocale )
当以JSON格式查看时,UIVendor对象如下所示:
- var vendor =
- {
- vendorId: 123,vendorName: "ABC Company",emails : [
- { emailAddress: "abc123@abc.com",flags: 2 },{ emailAddress: "xyz@abc.com",flags: 3 }
- ]
- }
UIVendor bean有一个名为“Emails”的字段,类型为ArrayList,带有适当的setter和getter(getEmails / setEmails)。 NotificationEmail对象也具有适当的公共setter / getter。
当我尝试使用以下代码发布对象时:
- $.post("ajax/saveVendor.do",$.param(vendor),saveEntityCallback,"json" );
我在日志中收到此错误:
- 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作为单独的字段公开:
- package com.mycompany.beans;
- import java.util.*;
- import org.apache.commons.lang.*;
- import com.mycompany.domain.Vendor;
- import com.mycompany.domain.VendorAttributes;
- import org.apache.commons.logging.*;
- import org.codehaus.jackson.annotate.JsonIgnore;
- public class UIVendor
- {
- private final Log logger = LogFactory.getLog( this.getClass() );
- private Vendor vendor;
- private boolean ftpFlag;
- private String ftpHost;
- private String ftpPath;
- private String ftpUser;
- private String ftpPassword;
- private List<UINotificationEmail> emails = null;
- public UIVendor() { this( new Vendor() ); }
- public UIVendor( Vendor vendor )
- {
- this.vendor = vendor;
- loadVendorAttributes();
- }
- private void loadVendorAttributes()
- {
- this.ftpFlag = false;
- this.ftpHost = this.ftpPassword = this.ftpPath = this.ftpUser = "";
- this.emails = null;
- for ( VendorAttributes a : this.vendor.getVendorAttributes() )
- {
- String key = a.getVendorFakey();
- String value = a.getVendorFaValue();
- int flags = a.getFlags();
- if ( StringUtils.isBlank(key) || StringUtils.isBlank(value) ) continue;
- if ( key.equals( "ftpFlag" ) )
- {
- this.ftpFlag = BooleanUtils.toBoolean( value );
- }
- else if ( key.equals( "ftpHost" ) )
- {
- this.ftpHost = value;
- }
- else if ( key.equals("ftpPath") )
- {
- this.ftpPath = value;
- }
- else if ( key.equals("ftpUser") )
- {
- this.ftpUser = value;
- }
- else if ( key.equals("ftpPassword") )
- {
- this.ftpPassword = value;
- }
- else if ( key.equals("email") )
- {
- UINotificationEmail email = new UINotificationEmail(value,flags);
- this.getEmails().add( email );
- }
- }
- }
- private void saveVendorAttributes()
- {
- int id = this.vendor.getVendorId();
- List<VendorAttributes> attrs = this.vendor.getVendorAttributes();
- attrs.clear();
- if ( this.ftpFlag )
- {
- VendorAttributes flag = new VendorAttributes();
- flag.setVendorId( id );
- flag.setStatus( "A" );
- flag.setVendorFakey( "ftpFlag" );
- flag.setVendorFaValue( BooleanUtils.toStringTrueFalse( this.ftpFlag ) );
- attrs.add( flag );
- if ( StringUtils.isNotBlank( this.ftpHost ) )
- {
- VendorAttributes host = new VendorAttributes();
- host.setVendorId( id );
- host.setStatus( "A" );
- host.setVendorFakey( "ftpHost" );
- host.setVendorFaValue( this.ftpHost );
- attrs.add( host );
- if ( StringUtils.isNotBlank( this.ftpPath ) )
- {
- VendorAttributes path = new VendorAttributes();
- path.setVendorId( id );
- path.setStatus( "A" );
- path.setVendorFakey( "ftpPath" );
- path.setVendorFaValue( this.ftpPath );
- attrs.add( path );
- }
- if ( StringUtils.isNotBlank( this.ftpUser ) )
- {
- VendorAttributes user = new VendorAttributes();
- user.setVendorId( id );
- user.setStatus( "A" );
- user.setVendorFakey( "ftpUser" );
- user.setVendorFaValue( this.ftpUser );
- attrs.add( user );
- }
- if ( StringUtils.isNotBlank( this.ftpPassword ) )
- {
- VendorAttributes password = new VendorAttributes();
- password.setVendorId( id );
- password.setStatus( "A" );
- password.setVendorFakey( "ftpPassword" );
- password.setVendorFaValue( this.ftpPassword );
- attrs.add( password );
- }
- }
- }
- for ( UINotificationEmail e : this.getEmails() )
- {
- logger.debug("Adding email " + e );
- VendorAttributes email = new VendorAttributes();
- email.setStatus( "A" );
- email.setVendorFakey( "email" );
- email.setVendorFaValue( e.getEmailAddress() );
- email.setFlags( e.getFlags() );
- email.setVendorId( id );
- attrs.add( email );
- }
- }
- @JsonIgnore
- public Vendor getVendor()
- {
- saveVendorAttributes();
- return this.vendor;
- }
- public int getVendorId()
- {
- return this.vendor.getVendorId();
- }
- public void setVendorId( int vendorId )
- {
- this.vendor.setVendorId( vendorId );
- }
- public String getVendorType()
- {
- return this.vendor.getVendorType();
- }
- public void setVendorType( String vendorType )
- {
- this.vendor.setVendorType( vendorType );
- }
- public String getVendorName()
- {
- return this.vendor.getVendorName();
- }
- public void setVendorName( String vendorName )
- {
- this.vendor.setVendorName( vendorName );
- }
- public String getStatus()
- {
- return this.vendor.getStatus();
- }
- public void setStatus( String status )
- {
- this.vendor.setStatus( status );
- }
- public boolean isFtpFlag()
- {
- return this.ftpFlag;
- }
- public void setFtpFlag( boolean ftpFlag )
- {
- this.ftpFlag = ftpFlag;
- }
- public String getFtpHost()
- {
- return this.ftpHost;
- }
- public void setFtpHost( String ftpHost )
- {
- this.ftpHost = ftpHost;
- }
- public String getFtpPath()
- {
- return this.ftpPath;
- }
- public void setFtpPath( String ftpPath )
- {
- this.ftpPath = ftpPath;
- }
- public String getFtpUser()
- {
- return this.ftpUser;
- }
- public void setFtpUser( String ftpUser )
- {
- this.ftpUser = ftpUser;
- }
- public String getFtpPassword()
- {
- return this.ftpPassword;
- }
- public void setFtpPassword( String ftpPassword )
- {
- this.ftpPassword = ftpPassword;
- }
- public List<UINotificationEmail> getEmails()
- {
- if ( this.emails == null )
- {
- this.emails = new ArrayList<UINotificationEmail>();
- }
- return emails;
- }
- public void setEmails(List<UINotificationEmail> emails)
- {
- this.emails = emails;
- }
- }
更新2
这是杰克逊的输出:
- {
- "vendorName":"MAIL","vendorId":45,"emails":
- [
- {
- "emailAddress":"dfg","success":false,"failure":false,"flags":0
- }
- ],"vendorType":"DFG","ftpFlag":true,"ftpHost":"kdsfjng","ftpPath":"dsfg","ftpUser":"sdfg","ftpPassword":"sdfg","status":"A"
- }
这是我在POST上返回的对象的结构:
- {
- "vendorId":"45","vendorName":"MAIL","status":"A","emails":
- [
- {
- "success":"false","failure":"false","emailAddress":"dfg"
- },{
- "success":"true","failure":"true","emailAddress":"pfc@sj.org"
- }
- ]
- }
我也尝试过使用www.json.org上的JSON库进行序列化,结果就是你在上面看到的。但是,当我发布该数据时,传递给控制器的UIVendor对象中的所有字段都为空(尽管该对象不是)。
解决方法
- @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
- public @ResponseBody AjaxResponse saveVendor( @Valid @RequestBody UIVendor vendor,Locale currentLocale )
经过多次反复试验,我终于想出了问题所在。使用以下控制器方法签名时:
- @RequestMapping(value="/ajax/saveVendor.do",Locale currentLocale )
客户端脚本必须以后数据(通常是“application / x-www-form-urlencoded”)格式传递对象中的字段(即,field = value& field2 = value2)。这是在jQuery中完成的,如下所示:
- $.post( "mycontroller.do",$.param(object),callback,"json" )
这适用于没有子对象或集合的简单POJO对象,但是一旦为传递的对象引入了显着的复杂性,jQuery用于序列化对象数据的符号就不会被Spring的映射逻辑识别:
- object[0][field]
- @RequestMapping(value="/ajax/saveVendor.do",method = RequestMethod.POST)
- public @ResponseBody AjaxResponse saveVendor( @RequestBody UIVendor vendor,Locale currentLocale )
并将呼叫从客户端更改为:
- $.ajax(
- {
- url:"ajax/mycontroller.do",type: "POST",data: JSON.stringify( objecdt ),success: callback,dataType: "json",contentType: "application/json"
- } );
这需要使用JSON javascript库。它还强制将contentType设置为“application / json”,这是Spring在使用@RequestBody注释时所期望的,并将对象序列化为Jackson可以反序列化为有效对象结构的格式。
唯一的副作用是现在我必须在控制器方法中处理我自己的对象验证,但这相对简单:
- BindingResult result = new BeanPropertyBindingResult( object,"MyObject" );
- Validator validator = new MyObjectValidator();
- validator.validate( object,result );
如果有人有任何改进这个过程的建议,我会全力以赴。