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

前端之家收集整理的这篇文章主要介绍了jquery – 使用JSON将嵌套对象发布到Spring MVC控制器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个控制器,POST处理程序定义如下:
@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对象中的所有字段都为空(尽管该对象不是)。

解决方法

更新:从Spring 3.1开始,可以使用 @Valid On @RequestBody Controller Method Arguments
@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 );

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

猜你在找的jQuery相关文章