Post Objeto anidado al controlador Spring MVC usando JSON

Tengo un controlador con el controlador POST definido así:

@RequestMapping(value="/ajax/saveVendor.do", method = RequestMethod.POST)
public @ResponseBody AjaxResponse saveVendor( @Valid UIVendor vendor,
                                              BindingResult result,
                                              Locale currentLocale )

El objeto UIVendor, cuando se ve en formato JSON, se ve así:

var vendor = 
{
  vendorId: 123,
  vendorName: "ABC Company",
  emails : [
             { emailAddress: "[email protected]", flags: 2 },
             { emailAddress: "[email protected]", flags: 3 }
           ]
}

El bean UIVendor tiene un campo llamado "Emails" del tipo ArrayList, con los configuradores y captadores apropiados (getEmails / setEmails). El objeto NotificationEmail también tiene los setters / getters públicos apropiados.

Cuando intento publicar el objeto con el siguiente código:

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

Recibo este error en los registros:

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 [[email protected]]

¿Cómo publico correctamente un objeto anidado como este en un controlador Spring y hago que se deserialice correctamente en la estructura de objeto adecuada.

ACTUALIZA Por solicitud de Bohzo, aquí está el contenido de la clase UIVendor. Esta clase envuelve una clase de bean generada por el servicio web, exponiendo los VendorAttributes como campos individuales:

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;
  }
}

UPDATE 2 Aquí está la salida de Jackson.

{
  "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"
}

Y aquí está la estructura del objeto que estoy devolviendo en la POST:

{
  "vendorId":"45",
  "vendorName":"MAIL",
  "vendorType":"DFG",
  "ftpFlag":true,
  "ftpHost":"kdsfjng",
  "ftpUser":"sdfg",
  "ftpPath":"dsfg",
  "ftpPassword":"sdfg",
  "status":"A",
  "emails": 
            [
              {
                "success":"false",
                "failure":"false",
                "emailAddress":"dfg"
              },
              {
                "success":"true",
                "failure":"true",
                "emailAddress":"[email protected]"
              }
            ]
}

También he intentado serializar usando la biblioteca JSON de www.json.org, y el resultado es exactamente lo que ves arriba. Sin embargo, cuando publico esos datos, todos los campos en el objeto UIVendor pasado al controlador son nulos (aunque el objeto no lo es).

Respuestas a la pregunta(4)

Su respuesta a la pregunta