ZetCode

Spring Boot JSON

最后修改于 2023 年 7 月 28 日

Spring Boot JSON 教程展示了如何在 Spring Boot 注解中提供 JSON 数据。

Spring 是一个流行的 Java 应用程序框架,而 Spring Boot 是 Spring 的一个演进,它有助于轻松创建独立的、生产级的基于 Spring 的应用程序。

JSON

JSON (JavaScript 对象表示法) 是一种轻量级的数据交换格式。它易于人类阅读和编写,也易于机器解析和生成。JSON 的官方互联网媒体类型是 application/json。JSON 文件扩展名是 .json

Spring Boot JSON

Spring Boot 提供了与三个 JSON 映射库的集成

Jackson 是首选和默认的库。

spring.http.converters.preferred-json-mapper=jsonb

可以使用 spring.http.converters.preferred-json-mapper 属性设置首选的 JSON 转换器。

Jackson

Jackson 是一个用于 Java 的数据处理工具套件。它允许以 JSON、Avro、BSON、CBOR、CSV、Smile、(Java)Properties、Protobuf、XML 或 YAML 格式读写数据。

Jackson 是自动配置的。它附带了 spring-boot-starter-json。当 Jackson 位于类路径上时,会自动配置一个 ObjectMapper bean。spring-boot-starter-jsonspring-boot-starter-web 一起被引入。

在 Spring 中,对象使用 Jackson 库自动转换为 JSON。Spring 也可以配置为转换为 XML。

spring.jackson.date-format= # For instance, `yyyy-MM-dd HH:mm:ss`.
spring.jackson.default-property-inclusion= # including properties during serialization. 
spring.jackson.deserialization.*= # Jackson on/off features for deserialization.
spring.jackson.generator.*= # Jackson on/off features for generators.
spring.jackson.joda-date-time-format= # Joda date time format string.
spring.jackson.locale= # Locale used for formatting.
spring.jackson.mapper.*= # Jackson general purpose on/off features.
spring.jackson.parser.*= # Jackson on/off features for parsers.
spring.jackson.property-naming-strategy= # PropertyNamingStrategy.
spring.jackson.serialization.*= # Jackson on/off features for serialization.
spring.jackson.time-zone= #  Time zone
spring.jackson.visibility.*= # To limit which methods (and fields) are auto-detected.

Jackson 可以使用应用程序属性进行配置。

@Configuration
public class WebConfig 
{
    @Bean
    public ObjectMapper customJson() {

        return new Jackson2ObjectMapperBuilder()
            .indentOutput(true)
            .propertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE)
            .build();
    }
}

Jackson 可以使用 Jackson2ObjectMapperBuilder 进行配置。

@Bean
public Jackson2ObjectMapperBuilderCustomizer customJson()
{
    return builder -> {

        builder.indentOutput(true);
        builder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    };
}

可以使用 Jackson2ObjectMapperBuilderCustomizer 修改现有配置。

Spring Boot JSON 示例

以下应用程序将 JSON 数据返回给客户端。Jackson 以三种不同的方式配置。

build.gradle 
...
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───config
│   │           │       WebConfig.java
│   │           ├───controller
│   │           │       MyController.java
│   │           ├───model
│   │           │       City.java
│   │           └───service
│   │                   CityService.java
│   │                   ICityService.java
│   └───resources
│           application.yml
└── test
    ├── java
    └── resources

这是项目结构。

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

这是 Gradle build.gradle 文件。

resources/application.yml
spring:
  main:
    log-startup-info: false

  jackson:
    property-naming-strategy: UPPER_CAMEL_CASE
    serialization:
      indent-output: true

application.yml 文件中,我们设置 Jackson 属性。这些设置可以使用配置 bean 进行覆盖或自定义。

com/zetcode/model/City.java
package com.zetcode.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Objects;

public class City {

    @JsonIgnore
    private Long id;

    private String name;
    private int population;

    public City() {
    }

    public City(Long id, String name, int population) {

        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + Objects.hashCode(this.id);
        hash = 79 * hash + Objects.hashCode(this.name);
        hash = 79 * hash + this.population;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final City other = (City) obj;
        if (this.population != other.population) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        return Objects.equals(this.id, other.id);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("City{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", population=").append(population);
        sb.append('}');
        return sb.toString();
    }
}

我们有一个 City 模型类。

@JsonIgnore
private Long id;

使用 @JsonIgnore,我们从 JSON 序列化中删除 id

com/zetcode/service/ICityService.java
package com.zetcode.service;

import com.zetcode.model.City;

import java.util.List;

public interface ICityService {

    List<City> getCities();
}

ICityService 包含 getCities 合约方法。

com/zetcode/service/CityService.java
package com.zetcode.service;

import com.zetcode.model.City;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class CityService implements ICityService {

    public List<City> getCities() {

        List<City> cities = new ArrayList<>();

        cities.add(new City(1L, "Bratislava", 432000));
        cities.add(new City(2L, "Budapest", 1759000));
        cities.add(new City(3L, "Prague", 1280000));
        cities.add(new City(4L, "Warsaw", 1748000));
        cities.add(new City(5L, "Los Angeles", 3971000));
        cities.add(new City(6L, "New York", 8550000));
        cities.add(new City(7L, "Edinburgh", 464000));
        cities.add(new City(8L, "Berlin", 3671000));

        return cities;
    }
}

CityService 返回一个城市对象列表。我们没有使用数据库来简化示例。

com/zetcode/config/WebConfig.java
package com.zetcode.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

@Configuration
public class WebConfig {

    //    @Bean
    public ObjectMapper configureJson() {
        return new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .propertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE)
                .build();
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizeJson() {
        return builder -> {

            builder.indentOutput(true);
            builder.propertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE);
        };
    }
}

WebConfig 中,我们有覆盖和自定义 Jackson 设置的 bean。尝试不同的设置并查看它们的应用方式。启用或禁用 bean(通过注释 @Bean)并比较结果。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class MyController {

    private final ICityService cityService;

    public MyController(ICityService cityService) {
        this.cityService = cityService;
    }

    @GetMapping("/cities")
    public List<City> getCities() {

        return cityService.getCities();
    }
}

为了将数据写入响应正文,我们使用 @RestController@Controller/@ResponseBody 组合。

@GetMapping("/cities")
public List<City> getCities() {

    return cityService.getCities();
}

getCities 方法返回一个城市对象列表。这些对象会自动序列化为 JSON。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application  {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 是设置 Spring Boot 应用程序的入口点。

我们使用 ./gradlew bootRun 运行应用程序。

$ curl localhost:8080/cities
[ {
  "Name" : "Bratislava",
  "Population" : 432000
}, {
  "Name" : "Budapest",
  "Population" : 1759000
}, {
  "Name" : "Prague",
  "Population" : 1280000
}, {
  "Name" : "Warsaw",
  "Population" : 1748000
}, {
  "Name" : "Los Angeles",
  "Population" : 3971000
}, {
  "Name" : "New York",
  "Population" : 8550000
}, {
  "Name" : "Edinburgh",
  "Population" : 464000
}, {
  "Name" : "Berlin",
  "Population" : 3671000
} ]

这是一个示例输出。JSON 数据被缩进,并且属性命名策略是 UPPER_CAMEL_CASE。

在本文中,我们展示了如何在 Spring Boot 应用程序中向客户端提供 JSON 数据。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程