跳到主要内容

6.14 使用 Jackson 的 JSON 绑定

目前最常见的数据交换格式是 JSON。

事实上,Controller 注解中的默认值指定 Micronaut 中的控制器默认处理和响应 JSON。

为了以非阻塞的方式实现这一点,Micronaut 基于 Jackson 异步 JSON 解析 API 和 Netty 的基础上构建,从而以非阻塞方式读取传入的 JSON。

通过响应式框架绑定

然而,从开发人员的角度来看,你通常只能使用普通的旧 Java 对象(Plain Old Java Objects,POJO),并且可以选择使用 RxJavaProject Reactor 等响应式框架。以下是一个控制器的示例,该控制器以非阻塞的方式从 JSON 读取并保存传入的 POJO:

使用响应式流读取 POJO

@Controller("/people")
public class PersonController {

Map<String, Person> inMemoryDatastore = new ConcurrentHashMap<>();

@Post("/saveReactive")
@SingleResult
public Publisher<HttpResponse<Person>> save(@Body Publisher<Person> person) { // (1)
return Mono.from(person).map(p -> {
inMemoryDatastore.put(p.getFirstName(), p); // (2)
return HttpResponse.created(p); // (3)
}
);
}

}
  1. 该方法接收一个发布器,该发布器在读取 JSON 后发出 POJO
  2. map 方法将实例存储在 Map
  3. 返回 HttpResponse

使用命令行中的 cURL,可以将 JSON POST 到 /people URI:

使用 cURL 发送 JSON

$ curl -X POST localhost:8080/people -d '{"firstName":"Fred","lastName":"Flintstone","age":45}'

使用 CompletableFuture 绑定

也可以使用CompletableFuture API编写与上一示例相同的方法:

使用 CompletableFuture 读取 JSON

@Controller("/people")
public class PersonController {

Map<String, Person> inMemoryDatastore = new ConcurrentHashMap<>();

@Post("/saveFuture")
public CompletableFuture<HttpResponse<Person>> save(@Body CompletableFuture<Person> person) {
return person.thenApply(p -> {
inMemoryDatastore.put(p.getFirstName(), p);
return HttpResponse.created(p);
}
);
}

}

上面的例子使用 thenApply 方法来实现与前面的例子相同的效果。


使用 POJO 绑定

然而,你可以同样简单的这样写:

绑定 JSON POJO

@Controller("/people")
public class PersonController {

Map<String, Person> inMemoryDatastore = new ConcurrentHashMap<>();

@Post
public HttpResponse<Person> save(@Body Person person) {
inMemoryDatastore.put(person.getFirstName(), person);
return HttpResponse.created(person);
}

}

Micronaut 只在以非阻塞方式读取数据后才执行你的方法。

提示

Jackson 产生的输出可以通过多种方式进行定制,从定义 Jackson 模块到使用 Jackson 注解


Jackson 配置

Jackson ObjectMapper 可以通过使用 JacksonConfiguration 类进行配置来进行配置。

所有 Jackson 配置键都以 jackson 开头。

dateFormatString日期格式
localeString使用 Locale.forLanguageTag。示例:en-US
timeZoneString使用 TimeZone.getTimeZone。示例:PST
serializationInclusionString一种 JsonInclude.Include。示例:ALWAYS
propertyNamingStrategyStringPropertyNamingStrategy 实体名字。示例:SNAKE_CASE
defaultTypingString枚举 defaultTypeingObjectMapper.DefaultTyping 中多态类型处理的全局。示例:NON_FINAL

示例:

jackson:
serializationInclusion: ALWAYS

特性

所有功能都可以使用其名称作为键和布尔值进行配置,以指示启用或禁用。

serializationMapSerializationFeature
deserializationMapDeserializationFeature
mapperMapMapperFeature
parserMapJsonParser.Feature
generatorMapJsonGenerator.Feature
factoryMapJsonFactory.Feature

示例:

jackson:
serialization:
indentOutput: true
writeDatesAsTimestamps: false
deserialization:
useBigIntegerForInts: true
failOnUnknownProperties: false

进一步自定义 JsonFactory

在某些情况下,你可能希望在功能配置之外自定义 ObjectMapper 使用的 JsonFactory(例如,允许自定义字符转义)。这可以通过提供自己的 JsonFactory bean 来实现,也可以通过提供 BeanCreatedEventListener<JsonFactory> 来在启动时配置默认 bean。

支持 @JsonView

如果在 application.yml 中将 jackson.json-view.enabled 设置为 true,则可以在控制器方法上使用 @JsonView 注解。

Jackson 的 @JsonView 注解允许你根据响应控制公开哪些属性。有关更多信息,参阅 Jackson JSON 视图

Bean

除了配置之外,还可以注册 bean 来定制 Jackson。扩展以下任何类的所有 bean 都会向对象映射器注册:

服务加载器

通过服务加载器注册的任何模块也会添加到默认的对象映射器中。

数字精度

在 JSON 解析过程中,框架可以将任何传入的数据转换为中间对象模型。默认情况下,此模型使用 BigIntegerlongdouble 作为数值。这意味着一些可以用 BigDecimal 表示的信息可能会丢失。例如,即使反序列化的目标类型使用 BigDecimal,也可能会截断具有许多无法用双精度表示的小数位数的数字。关于尾零的元数据(BigDecimal.precision()),例如 0.120.120 之间的差,也会被丢弃。

如果你需要完全准确的数字类型,请使用以下配置:

jackson:
deserialization:
useBigIntegerForInts: true
useBigDecimalForFloats: true

英文链接