【问题标题】:org.apache.struts2.json.JSONException: Incompatible types for propertyorg.apache.struts2.json.JSONException:属性类型不兼容
【发布时间】:2014-03-19 02:28:49
【问题描述】:

我通过 JSON-rpc 接收java.util.List<Object[]> 作为 JavaScript 数组,如下所示。

[
    [1, 0.10, 1.00],
    [2, 0.20, 2.00],
    [3, 0.30, 3.00],
    [4, 0.40, 4.00],
    [5, 0.50, 5.00],
    [6, 0.60, 6.00],
    [7, 0.70, 7.00],
    [8, 0.80, 8.00],
    [9, 0.90, 9.00],
    [10, 1.00, 10.00],
    [11, 1.10, 11.00],
    [12, 1.20, 12.00],
    [13, 1.30, 13.00],
    [14, 1.40, 14.00],
    [15, 1.50, 15.00],
    [16, 1.60, 16.00],
    [17, 1.70, 17.00],
    [18, 1.80, 18.00]
]

我需要将相同的数组传回服务器(在最后一维稍作修改)。

我使用下面的函数发回这个数组。

var request;
var timeout;
var itemsArray=[];

function insert()
{
    if(!request)
    {
        var i=0;

        $('input[name="txtCharge[]"]').each(function()
        {
            isNaN($(this).val())||$(this).val()===''?itemsArray[i][2]='':itemsArray[i][2]=eval(eval($(this).val()).toFixed(2));
            i++;
        });

        request = $.ajax({
            dataType:"json",
            type: "POST",
            data: JSON.stringify({jsonrpc:'2.0', method:'insertZoneCharges', id:'jsonrpc', params:[itemsArray]}),
            contentType: "application/json-rpc; charset=utf-8",
            url: "AddZoneChargeList",

            success: function(response)
            {
                alert(response.result);
            },
            complete: function()
            {
                timeout = request = null;
            },
            error: function(request, status, error)
            {
                if(status!=="timeout"&&status!=="abort")
                {
                    alert(status+" : "+error);
                }
            }
        });
        timeout2 = setTimeout(function() {
            if(request)
            {
                request.abort();
                alert("The request has been timed out.");
            }
        }, 300000);
    }
}

循环后数组itemsArray的结构和第一个sn-p中提到的完全一样。

这个jQuery函数要调用的方法如下。

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="json-default")
public final class ZoneCharge extends ActionSupport implements Serializable
{
    private static final long serialVersionUID = 1L;

    public ZoneCharge() {}

    //This method is invoked by the given jQuery function. 
    //It should accept the array as a parameter of type List<Object[]> but it doesn't.
    @SMDMethod
    public String insertZoneCharges(@SMDMethodParameter(name="list")List<Object[]> list)
    {
        for(Object[]o:list)
        {
            System.out.println(o[0]+" : "+o[1]+" : "+o[2]);
        }
        return "The action completed successfully.";
    }

    @Action(value = "AddZoneChargeList",
    results = {
        @Result(name = ActionSupport.SUCCESS, type = "json", params = {"enableSMD", "true"})},
    interceptorRefs = {
        @InterceptorRef(value = "json", params = {"enableSMD", "true"})})
    public String insertAction() throws Exception {
        return ActionSupport.SUCCESS;
    }
}

当尝试发出请求时,会引发以下异常。

Feb 19, 2014 6:14:00 AM org.apache.struts2.json.rpc.RPCError error
SEVERE: Incompatible types for property insertZoneCharges
org.apache.struts2.json.JSONException: Incompatible types for property insertZoneCharges
    at org.apache.struts2.json.JSONPopulator.convertToCollection(JSONPopulator.java:254)
    at org.apache.struts2.json.JSONPopulator.convert(JSONPopulator.java:131)
    at org.apache.struts2.json.JSONInterceptor.invoke(JSONInterceptor.java:242)
    at org.apache.struts2.json.JSONInterceptor.intercept(JSONInterceptor.java:133)
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
    at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:562)
    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at filter.NoCacheFilter.doFilter(NoCacheFilter.java:25)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:105)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:144)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:119)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1822)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)

为什么该数组没有映射到List&lt;Object[]&gt;,当相同的List&lt;Object[]&gt;在接收来自服务器的响应时被正确映射到一个数组(这是由一个单独的jQuery函数完成的,这个没有涉及问题)?


编辑 1:

下面一行,

data: JSON.stringify({jsonrpc:'2.0', method:'insertZoneCharges', id:'jsonrpc', params:[itemsArray]})

在给定的jQuery函数中对应如下字符串。

{
    "jsonrpc": "2.0",
    "method": "insertZoneCharges",
    "id": "jsonrpc",
    "params": [
        [
            [1, 0.1, 1],
            [2, 0.2, 2],
            [3, 0.3, 3],
            [4, 0.4, 4],
            [5, 0.5, 5],
            [6, 0.6, 6],
            [7, 0.7, 7],
            [8, 0.8, 8],
            [9, 0.9, 9],
            [10, 1, 10],
            [11, 1.1, 11],
            [12, 1.2, 12],
            [13, 1.3, 13],
            [14, 1.4, 14],
            [15, 1.5, 15],
            [16, 1.6, 16],
            [17, 1.7, 17],
            [18, 1.8, 18]
        ]
    ]
}

这似乎有效,应该正确映射到java.util.List&lt;Object[]&gt;,而不会导致异常。


编辑 2:

当泛型类型参数被删除以便列表只是List(而不是&lt;List&lt;Object[]&gt;&gt;)或参数类型为List&lt;List&lt;Object&gt;&gt; 时,此方法有效。类似的,

@SMDMethod
public String insertZoneCharges(@SMDMethodParameter(name="list")List<List<Object>> list)
{
    for(List<Object> o:list)
    {
        System.out.println(o);
    }
    return "The action completed successfully.";
}

循环显示以下输出。

[1, 0.1, 1]
[2, 0.2, 2]
[3, 0.3, 3]
[4, 0.4, 4]
[5, 0.5, 5]
[6, 0.6, 6]
[7, 0.7, 7]
[8, 0.8, 8]
[9, 0.9, 9]
[10, 1, 10]
[11, 1.1, 11]
[12, 1.2, 12]
[13, 1.3, 13]
[14, 1.4, 14]
[15, 1.5, 15]
[16, 1.6, 16]
[17, 1.7, 17]
[18, 1.8, 18]

查看输出,JSON 数组应该正确映射到 List&lt;Object[]&gt;,但它没有发生。


编辑 3:(不是实质性编辑)。

虽然实际数组在给出的 jQuery 函数中是不可见的,但可以通过以下 jQuery 函数简单地重现。

function insert()
{
    var a=[[1, 2], [3, 4]];  //Array.

    $.ajax({
            dataType:"json",
            type: "POST",
            data: JSON.stringify({jsonrpc:'2.0', method:'insertZoneCharges', id:'jsonrpc', params:[a]}),
            contentType: "application/json-rpc; charset=utf-8",
            url: "AddZoneChargeList",
            success: function(response)
            {
                alert(response.result);
            },
            complete: function()
            {
                //Do something.
            },
            error: function(request, status, error)
            {
                //Do something.
            }
        });
    }
}

并且SMD方法的预期方法参数可能是java.util.List&lt;Object[]&gt;java.util.List&lt;Long[]&gt;。无论哪种情况,它都会失败并给出异常。


剩下最后一件事。如果有像(在 JavaScript 中)这样的一维数组,

var a=[1, 2];

那么这将作为 SMD 方法参数正确映射到 Long[](以及 Object[])。

所以,现在我看不到去哪里了。

【问题讨论】:

  • 可能json拦截器使用与params拦截器相同的概念来映射它适用的对象,不是吗?
  • 其他类型的参数,如原始类型(和/或它们的包装类型)、字符串...被正确映射。因此,恕我直言,它没有。
  • 如果有人有,请添加答案。

标签: jquery json struts2 json-rpc struts2-json-plugin


【解决方案1】:

使用来源,卢克。如果您查看抛出该异常的类,您会发现它不支持List&lt;anything[]&gt;。来自org.apache.struts2.json.JSONPopulator.convertToCollection()的相关线路:

232               // create an object for each element
233               for (int j = 0; j < values.size(); j++) {
234                   Object listValue = values.get(j);
235   
236                   if (itemClass.equals(Object.class)) {
237                       // Object[]
238                       newCollection.add(listValue);
239                   } else if (isJSONPrimitive(itemClass)) {
240                       // primitive array
241                       newCollection.add(this.convertPrimitive(itemClass, listValue, accessor));
242                   } else if (Map.class.isAssignableFrom(itemClass)) {
243                       Object newObject = convertToMap(itemClass, itemType, listValue, accessor);
244                       newCollection.add(newObject);
245                   } else if (List.class.isAssignableFrom(itemClass)) {
246                       Object newObject = convertToCollection(itemClass, itemType, listValue, accessor);
247                       newCollection.add(newObject);
248                   } else if (listValue instanceof Map) {
249                       // array of beans
250                       Object newObject = itemClass.newInstance();
251                       this.populateObject(newObject, (Map) listValue);
252                       newCollection.add(newObject);
253                   } else
254                       throw new JSONException("Incompatible types for property " + accessor.getName());
255               }

此代码将项目添加到列表中,并在决定如何从数据中实例化对象之前经过一系列类型检查。第 237 行的评论具有误导性;它似乎是从convertToArray 复制和粘贴的;它所做的只是将当前的 listValue 添加到列表中。同样,第 249 行关于数组的注释也没有创建数组。事实上,这里没有处理数组的代码,所以它直接跳到第 254 行并抛出你看到的异常。该代码似乎缺少几行,如下所示:

else if (itemClass.isArray()) {
   Object newObject = convertToArray(itemClass, itemType, listValue, method);
   newCollection.add(newObject);

...直接从convert() 中的代码转换为处理一维数组 - 正如您所注意到的那样,它可以工作。如果这是疏忽还是故意的,您必须询问 struts 开发人员。看起来您确实受困于集合的集合。

【讨论】:

  • 非常感谢您的回答。在documentation 中,我没有看到任何关于List&lt;anything[]&gt; 的信息。因此,似乎是故意的。另一方面,一个明显的问题是如何使这个插件进程成为这样的结构,如果不是疏忽的话。很难看到插件开发人员自己会犯这样的错误(令人困惑:List&lt;Object[]&gt; 在检索时被正确映射到 JavaScript 数组(如问题中所示),但反之则不正确——同时将相同的数组发送回服务器)
  • 鉴于 struts 不支持数组列表,您有两个选择:按照我上面指出的方式修补插件,或者编写您的 API 来处理 List,可能调用内部List 方法,通过在每个元素上使用 list.toArray() 进行转换。我感受到了你的痛苦,10 年前我在 apache 轴遇到类似问题后选择了第一个选项。
  • 这很笨拙,因为即使这样做也可能会导致副作用(可能是因为这个原因不支持)。它或多或少是实验性的,当然,我不应该自己做,因为我没有那么敏锐的眼光来研究 API :)
  • 稍后,当我找到解决方法时,我会接受答案(同时,如果您有解决方法,请不要忘记更新答案)。谢谢。
【解决方案2】:

你映射的参数不正确,正确的映射应该是

@SMDMethod
public String insertZoneCharges(@SMDMethodParameter(name="list")Object[] list)

【讨论】:

  • 这可行,但是我们必须单独处理对象数组中的逗号分隔值,如问题中的 EDIT 2 中所示。将 JavaScript 数组映射到简单的 Object[]List 而没有适当的泛型类型参数而不是将其映射到像 List&lt;Object[]&gt; 这样的正确类型,难道不应该是错误的吗?这应该是一个疏忽。不应该吗?如果您发现这是一个疏忽,您能否向 Struts 人员报告?
  • 小小,JavaScript 是一种通用类型语言,它没有 List 类型,除非您的问题中没有使用它。您必须将 javascript 数组 [[]] 映射到 Java 中的适当结构,它绝对应该是一个数组,而不是一个列表。在 Java 中,您可以将 Object[] 转换为 Object,它用于外部数组,但是数组的元素转换为 List&lt;Object&gt;。我不知道是否可以使用 Hibernate 中的某些结果转换器将 Object[List&lt;Object&gt;] 转换为 List&lt;Object[]&gt; 或在 for 循环中编写您自己的转换器。所以,这取决于你。
  • ... 请注意,您返回一个字符串,但您可以返回Object[],这是使用 json-rpc 的唯一原因。想象一下,您想在 Java 端编写一个排序操作,然后您只需通过 Ajax 传递数据并返回排序后的数据。如果您有特定于语言环境的数据,那么在 javascript 中做同样的事情是很困难的。
猜你喜欢
  • 2021-12-06
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 2021-10-01
相关资源
最近更新 更多