将Spring MVC投影与JSON和Jackson结合使用


问题内容

在新版本的Spring Data(Fowler)中,可以将接口传递给Spring MVC控制器动作,然后Spring
Data将自动创建代理实现并将值绑定到该代理对象。

博客文章中提供一个示例,描述了Spring Data Fowler中的一些新功能:

interface Form {
    @NotBlank String getName();
    @NotBlank String getText();
}

@Controller
@RequestMapping(value = "/guestbook")
class GuestbookController {
    @RequestMapping(method = RequestMethod.GET)
    String guestbook(Form form, Model model) { ... }

    @RequestMapping(method = RequestMethod.POST)
    String guestbook(@Valid Form form, Errors errors, Model model) { ... }
}

我的问题是,在用Jackson进行JSON反序列化时是否也可以做到这一点?例如,像这样:

interface Form {
    @NotBlank String getName();
    @NotBlank String getText();
}

@Controller
@RequestMapping(value = "/guestbook")
class GuestbookController {
    @RequestMapping(method = RequestMethod.POST)
    String guestbook(@Valid @RequestBody Form form) { ... }
}

但是,这给出了以下例外:

无法构造Form的实例,出现问题:抽象类型要么需要映射到具体类型,要么具有自定义反序列化器,要么被其他类型信息实例化

我知道问题出在哪里,但是有没有解决方案不需要我创建实现我的接口的类或编写大量代码?一个比这种方法更简单的方法。因为否则我不妨采用DTO方法,但是如果我可以像示例中那样简单地使用接口,我只是觉得它很棒。

通过上述方法,我可以使用DTO类(或者避免使用JSON),但是使用类似博客文章示例中的接口将是一件很整洁的事情。但是,在反序列化JSON时,Jackson库是否有可能?


问题答案:

您可以使用Jackson Mr Bean模块,该模块正是您想要的。

要使用它,只需下载特定的依赖项并将其设置为基础ObjectMapper实例的模块即可:

import java.io.IOException;
import java.util.List;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.mrbean.MrBeanModule;

public class AbstractPojosExample {

    public interface Person {

        String getName();

        int getAge();

        Address getAddress();

        default String asString() {
            return String.format("%s, %d, %s", this.getName(), this.getAge(), this.getAddress().asString());
        }
    }

    public interface Address {

        String getStreet();

        String getCity();

        default String asString() {
            return String.format("%s, %s", this.getStreet(), this.getCity());
        }
    }

    private void run() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new MrBeanModule());

        String json = "[ { \"name\": \"John\", \"age\": 23, "
            + "\"address\": { \"street\": \"1001 5th Ave\", \"city\": \"New York\" } }, "
            + "{ \"name\": \"Jean Jacques\", \"age\": 26 , "
            + "\"address\": { \"street\": \"1001 Champs Elysees Blvd\", \"city\": \"Paris\" } }, "
            + "{ \"name\": \"Oscar Ruben\", \"age\": 54, "
            + "\"address\": { \"street\": \"1001 9th July Ave\", \"city\": \"Buenos Aires\" } } ]";
        System.out.println(json);

        List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});

        people.stream().map(Person::asString).forEach(System.out::println);
    }

    public static void main(String[] args) throws IOException {
        AbstractPojosExample example = new AbstractPojosExample();
        example.run();
    }
}

对于给定的json:

[
  {
    "name": "John",
    "age": 23,
    "address": {
      "street": "1001 5th Ave",
      "city": "New York"
    }
  },
  {
    "name": "Jean Jacques",
    "age": 26,
    "address": {
      "street": "1001 Champs Elysees Blvd",
      "city": "Paris"
    }
  },
  {
    "name": "Oscar Ruben",
    "age": 54,
    "address": {
      "street": "1001 9th July Ave",
      "city": "Buenos Aires"
    }
  }
]

上面的小程序产生以下输出:

John, 23, 1001 5th Ave, New York
Jean Jacques, 26, 1001 Champs Elysees Blvd, Paris
Oscar Ruben, 54, 1001 9th July Ave, Buenos Aires