我有一个定义了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具有一个ArrayList类型的名为“ Emails”的字段,带有适当的设置器和获取器(getEmails /
setEmails)。NotificationEmail对象也具有适当的公共设置器/获取器。
当我尝试使用以下代码发布对象时:
$.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
根据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",
"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":"pfc@sj.org"
}
]
}
我也尝试过使用来自www.json.org的JSON库进行序列化,其结果与您在上面看到的完全一样。但是,当我发布该数据时,传递给控制器的UIVendor对象中的所有字段均为空(尽管该对象不是)。
更新: 从Spring
3.1开始,可以在@RequestBody控制器方法参数中使用@Valid。
@RequestMapping(value="/ajax/saveVendor.do", method = RequestMethod.POST)
public @ResponseBody AjaxResponse saveVendor( @Valid @RequestBody UIVendor vendor,
BindingResult result,
Locale currentLocale )
经过多次尝试和错误,我终于尽我所能找出问题所在。使用以下控制器方法签名时:
@RequestMapping(value="/ajax/saveVendor.do", method = RequestMethod.POST)
public @ResponseBody AjaxResponse saveVendor( @Valid UIVendor vendor,
BindingResult result,
Locale currentLocale )
客户端脚本必须以后数据(通常为“ application / x-www-form-urlencoded”)格式(即field = value&field2
= value2)传递对象中的字段。这是在jQuery中完成的,如下所示:
$.post( "mycontroller.do", $.param(object), callback, "json" )
对于没有子对象或集合的简单POJO对象,这很好用,但是一旦您对要传递的对象引入了极大的复杂性,Spring的映射逻辑将无法识别jQuery用于序列化对象数据的符号:
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可以反序列化为有效对象结构的格式。
唯一的副作用是,现在我必须在controller方法内部处理自己的对象验证,但这相对简单:
BindingResult result = new BeanPropertyBindingResult( object, "MyObject" );
Validator validator = new MyObjectValidator();
validator.validate( object, result );
如果有人对这个过程有任何改进的建议,我非常高兴。
> 我还发布了我在mainactivity中迄今为止所做的尝试。JAVA 我不知道我做错了什么。但我只是得到了一个错误的回答。我引用这些示例来做一个内部json对象响应服务器的帖子。 Json格式:(已编辑) 主要活动。爪哇:(编辑) Logcat:
问题内容: 我正在尝试使用HTTPie进行分析以发送一些嵌套的JSON对象,但我找不到方法。很清楚如何发送JSON对象,而不发送嵌套对象,例如 {“ user”:{“ name”:“ john”“ age”:10}} 问题答案: 您可以通过传递整个JSON: 或者使用以下命令将原始JSON指定为值:
问题内容: 我正在使用Symfony 2开发一个项目,我已经建立了一个捆绑包来处理我的所有数据库服务,这些服务来回传递JSON数据。 我的问题/问题: 是否可以发布简单的JSON对象?目前,我通过给对象起一个名字来欺骗我的ajax调用的普通表单帖子,如果我不给它起一个名字,我似乎无法从Symfony请求对象中获取数据。 我希望能够使用一个服务包来处理来自AJAX调用或常规Symfony形式的数据。
问题内容: 我正在尝试将JSON数组发布到MVC控制器。但是,无论我尝试什么,都为0或null。 我有包含文本框的表。我需要所有这些文本框中的ID和值作为对象。 这是我的Javascript: 这是我的查看代码: 这是控制器即时通讯试图接收数据以: 我究竟做错了什么? 问题答案: 您的代码有很多问题。让我们从标记开始。您有一个表,该表的每一行中都包含隐藏字段。除了您已经对那些隐藏元素的属性进行了硬
我遵循了http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/将对象列表发布到我的Spring Controller。很快,当列表大小达到256时,我遇到了IndexOutOfBoundsException。 我找到了一个答案,它说在WebDataBinder中设置setAuto就可以解决这个问题。所以在我的控制器中,我
我有一个复选框的集合,每个复选框代表一个“产品”的顶部。我想传递一个修改的产品对象,勾选的浇头存储为selectedToppings。 我已经尝试了下面的代码,但不起作用。问题是我正在传递一个空的“product”对象,并且只存储复选框值(作为“selectedToppings”)。如何将“product”对象与“selectedToppings”一起传递? html(片段): 控制器: 实体: