【问题标题】:receiving json and deserializing as List of object at spring mvc controller在spring mvc控制器接收json并反序列化为对象列表
【发布时间】:2014-05-25 14:58:17
【问题描述】:

我的代码如下:

控制器

@RequestMapping(value="/setTest", method=RequestMethod.POST, consumes="application/json")
public @ResponseBody ModelMap setTest(@RequestBody List<TestS> refunds, ModelMap map) {
    for(TestS r : refunds) {
        System.out.println(r.getName());
    }
    // other codes
}

TestS pojo

public class TestS implements Serializable {
    private String name;
    private String age;
    //getter setter
}

Json 请求

var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
$.ajax({
    url: "/setTest",
    data: z,
    type: "POST",
    dataType:"json",
    contentType:'application/json'               
});

它给出了这个错误

java.lang.ClassCastException:java.util.LinkedHashMap 无法转换为 com.air.cidb.entities.TestS

我正在使用 spring 3.1.2 和 jackson 2.0.4

编辑:我想在我的控制器方法中接收 TestS 对象的列表,并处理它们。我无法找到我是否发送了错误的 json 或我的方法签名错误。

【问题讨论】:

标签: spring spring-mvc jackson


【解决方案1】:

我相信这会解决问题

var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
z = JSON.stringify(JSON.parse(z));
$.ajax({
    url: "/setTest",
    data: z,
    type: "POST",
    dataType:"json",
    contentType:'application/json'               
});

【讨论】:

    【解决方案2】:

    解决方案效果很好,

    public List<String> savePerson(@RequestBody Person[] personArray)  
    

    对于这个签名,你可以像邮递员一样传递Person数组

    [
    {
      "empId": "10001",
      "tier": "Single",
      "someting": 6,
      "anything": 0,
      "frequency": "Quaterly"
    }, {
      "empId": "10001",
      "tier": "Single",
      "someting": 6,
      "anything": 0,
      "frequency": "Quaterly"
    }
    ]
    

    别忘了加consumes标签:

    @RequestMapping(value = "/getEmployeeList", method = RequestMethod.POST, consumes="application/json", produces = "application/json")
    public List<Employee> getEmployeeDataList(@RequestBody Employee[] employeearray) { ... }
    

    【讨论】:

      【解决方案3】:

      对我来说,下面的代码有效,首先发送带有正确标题的 json 字符串

      $.ajax({
              type: "POST",
              url : 'save',
              data : JSON.stringify(valObject),
              contentType:"application/json; charset=utf-8",
              dataType:"json",
              success : function(resp){
                  console.log(resp);
              },
              error : function(resp){
                  console.log(resp);
              }
          });
      

      然后在 Spring 方面 -

      @RequestMapping(value = "/save", 
                      method = RequestMethod.POST,
                      consumes="application/json")
      public @ResponseBody String  save(@RequestBody ArrayList<KeyValue> keyValList) {
          //Saving call goes here
          return "";
      }
      

      这里的 KeyValue 是简单的 pojo,对应于你的 JSON 结构,你也可以根据需要添加产品,我只是返回字符串。

      我的json对象是这样的-

      [{"storedKey":"vc","storedValue":"1","clientId":"1","locationId":"1"},
       {"storedKey":"vr","storedValue":"","clientId":"1","locationId":"1"}]
      

      【讨论】:

        【解决方案4】:
        @RequestMapping(
                 value="person", 
                 method=RequestMethod.POST,
                 consumes="application/json",
                 produces="application/json")
        @ResponseBody
        public List<String> savePerson(@RequestBody Person[] personArray) {
            List<String> response = new ArrayList<String>();
            for (Person person: personArray) {
                personService.save(person);
                response.add("Saved person: " + person.toString());
            }
            return response;
        }
        

        我们可以使用如上所示的数组。

        【讨论】:

        • 这应该是公认的答案。以数组为主体,在控制器方法中将其转换为列表。无需包装类或更改 JSON 的格式。
        【解决方案5】:

        这是适合我的代码。关键是你需要一个包装类。

        public class Person {
        
            private String name;
            private Integer age;
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public Integer getAge() {
                return age;
            }
        
            public void setAge(Integer age) {
                this.age = age;
            }
        
            @Override
            public String toString() {
                return "Person [name=" + name + ", age=" + age + "]";
            }
        

        一个 PersonWrapper 类

        public class PersonWrapper {
        
            private List<Person> persons;
        
            /**
             * @return the persons
             */
            public List<Person> getPersons() {
                return persons;
            }
        
            /**
             * @param persons the persons to set
             */
            public void setPersons(List<Person> persons) {
                this.persons = persons;
            }
        }
        

        我的控制器方法

        @RequestMapping(value="person", method=RequestMethod.POST,consumes="application/json",produces="application/json")
            @ResponseBody
            public List<String> savePerson(@RequestBody PersonWrapper wrapper) {
                List<String> response = new ArrayList<String>();
                for (Person person: wrapper.getPersons()){
                personService.save(person);
                 response.add("Saved person: " + person.toString());
            }
                return response;
            }
        

        发送的请求是 POST 中的 json

        {"persons":[{"name":"shail1","age":"2"},{"name":"shail2","age":"3"}]}
        

        响应是

        ["Saved person: Person [name=shail1, age=2]","Saved person: Person [name=shail2, age=3]"]
        

        【讨论】:

        • 感谢您的回答。但我想知道为什么我们需要做一个包装器?为什么这不能使用地图来完成?
        • 这里我怎样才能以不同的方式命名persons标签 --> {"CustomName":[{"name":"shail1","age":"2"},{"name":" shail2","age":"3"}]}?
        【解决方案6】:

        这在您尝试的方式中是不可能的。 Jackson 解组处理在 类型擦除之后编译的java 代码。所以你的

        public @ResponseBody ModelMap setTest(@RequestBody List<TestS> refunds, ModelMap map) 
        

        真的只有

        public @ResponseBody ModelMap setTest(@RequestBody List refunds, ModelMap map) 
        

        (列表 arg 中没有泛型)。

        Jackson 在解组List 时创建的默认类型是LinkedHashMap。

        正如@Saint 所提到的,您可以通过为列表创建自己的类型来规避这种情况,如下所示:

        class TestSList extends ArrayList<TestS> { }
        

        然后将你的控制器签名修改为

        public @ResponseBody ModelMap setTest(@RequestBody TestSList refunds, ModelMap map) {
        

        【讨论】:

        • 这不是真的。 Spring 4 支持这一点。您始终可以通过反射获得带有实际类型参数的参数化方法参数。
        • 但是由于他们没有使用 Spring 4,因此您的这部分答案是合适的。删除类型擦除位。
        • @SotiriosDelimanolis 很好的提示,不知道。您能否将我引导到 spring 文档中提到它的部分或它被埋在源代码中的部分?
        • 我现在没有链接。对于反射部分,可以使用Method#getGenericParameterTypes()获取类型信息。至于杰克逊与之合作,MVC章节中必须有一个条目。
        猜你喜欢
        • 1970-01-01
        • 2011-02-26
        • 1970-01-01
        • 2011-06-13
        • 2016-07-29
        • 2011-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多