Micronaut 用 Jackson 进行 JSON 绑定

2023-03-06 14:46 更新

现在最常见的数据交换格式是 JSON。

实际上,Controller 注解中的默认值指定 Micronaut 中的控制器默认使用和生成 JSON。

为了以非阻塞方式执行此操作,Micronaut 构建在 Jackson 异步 JSON 解析 API 和 Netty 之上,以便以非阻塞方式读取传入的 JSON。

使用 Reactive Frameworks 绑定

然而,从开发人员的角度来看,您通常可以只使用普通旧式 Java 对象 (POJO),并且可以选择使用 Reactive 框架,例如 RxJava 或 Project Reactor。下面是一个控制器的例子,它以非阻塞的方式从 JSON 中读取并保存传入的 POJO:

使用 Reactive Streams 读取 JSON

 Java Groovy  Kotlin 
@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)
            }
    );
}

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

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

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

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

    internal var inMemoryDatastore: MutableMap<String, Person> = ConcurrentHashMap()

@Post("/saveReactive")
@SingleResult
fun save(@Body person: Publisher<Person>): Publisher<HttpResponse<Person>> { // (1)
    return Mono.from(person).map { p ->
        inMemoryDatastore[p.firstName] = p // (2)
        HttpResponse.created(p) // (3)
    }
}

}
  1. 该方法接收一个发布者,一旦读取 JSON,该发布者就会发出 POJO

  2. map 方法将实例存储在 Map 中

  3. 返回一个 HttpResponse

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

使用 cURL POST JSON

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

使用 CompletableFuture 绑定

与前面示例相同的方法也可以改用 CompletableFuture API 编写:

使用 CompletableFuture 读取 JSON

 Java Groovy  Kotlin 
@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);
            }
    );
}

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

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

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

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

    internal var inMemoryDatastore: MutableMap<String, Person> = ConcurrentHashMap()

@Post("/saveFuture")
fun save(@Body person: CompletableFuture<Person>): CompletableFuture<HttpResponse<Person>> {
    return person.thenApply { p ->
        inMemoryDatastore[p.firstName] = p
        HttpResponse.created(p)
    }
}

}

上面的例子使用thenApply方法实现的和前面的例子一样。

使用 POJO 进行绑定

但是请注意,您可以轻松地编写:

绑定 JSON POJO

 Java Groovy  Kotlin 
@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);
}

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

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

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

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

    internal var inMemoryDatastore: MutableMap<String, Person> = ConcurrentHashMap()

@Post
fun save(@Body person: Person): HttpResponse<Person> {
    inMemoryDatastore[person.firstName] = person
    return HttpResponse.created(person)
}

}

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

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

Jackson 配置

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

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

dateFormat

String

日期格式

locale

String

使用 Locale.forLanguageTag。例子: en-US

timeZone

String

使用 TimeZone.getTimeZone。例子: PST

serializationInclusion

String

JsonInclude.Include 之一。例子: ALWAYS

propertyNamingStrategy

String

PropertyNamingStrategy 实例的名称。例子: SNAKE_CASE

defaultTyping

String

用于枚举 ObjectMapper.DefaultTyping 的多态类型处理的全局 defaultTyping。例子: NON_FINAL

示例:

 Properties Yaml  Toml  Groovy  Hocon  JSON 
jackson.serializationInclusion=ALWAYS
jackson:
  serializationInclusion: ALWAYS
[jackson]
  serializationInclusion="ALWAYS"
jackson {
  serializationInclusion = "ALWAYS"
}
{
  jackson {
    serializationInclusion = "ALWAYS"
  }
}
{
  "jackson": {
    "serializationInclusion": "ALWAYS"
  }
}

特性

所有功能都可以配置为它们的名称作为键和一个布尔值来指示启用或禁用。

serialization

Map

SerializationFeature

deserialization

Map

DeserializationFeature

mapper

Map

MapperFeature

parser

Map

JsonParser.Feature

generator

Map

JsonGenerator.Feature

factory

Map

JsonFactory.Feature

示例:

 Properties Yaml  Toml  Groovy  Hocon  JSON 
jackson.serialization.indentOutput=true
jackson.serialization.writeDatesAsTimestamps=false
jackson.deserialization.useBigIntegerForInts=true
jackson.deserialization.failOnUnknownProperties=false
jackson:
  serialization:
    indentOutput: true
    writeDatesAsTimestamps: false
  deserialization:
    useBigIntegerForInts: true
    failOnUnknownProperties: false
[jackson]
  [jackson.serialization]
    indentOutput=true
    writeDatesAsTimestamps=false
  [jackson.deserialization]
    useBigIntegerForInts=true
    failOnUnknownProperties=false
jackson {
  serialization {
    indentOutput = true
    writeDatesAsTimestamps = false
  }
  deserialization {
    useBigIntegerForInts = true
    failOnUnknownProperties = false
  }
}
{
  jackson {
    serialization {
      indentOutput = true
      writeDatesAsTimestamps = false
    }
    deserialization {
      useBigIntegerForInts = true
      failOnUnknownProperties = false
    }
  }
}
{
  "jackson": {
    "serialization": {
      "indentOutput": true,
      "writeDatesAsTimestamps": false
    },
    "deserialization": {
      "useBigIntegerForInts": true,
      "failOnUnknownProperties": false
    }
  }
}

进一步自定义 JsonFactory

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

支持@JsonView

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

Jackson 的 @JsonView 注释允许您控制在每个响应的基础上公开哪些属性。

Beans

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

服务加载器

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

数字精度

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

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

 Properties Yaml  Toml  Groovy  Hocon  JSON 
jackson.deserialization.useBigIntegerForInts=true
jackson.deserialization.useBigDecimalForFloats=true
jackson:
  deserialization:
    useBigIntegerForInts: true
    useBigDecimalForFloats: true
[jackson]
  [jackson.deserialization]
    useBigIntegerForInts=true
    useBigDecimalForFloats=true
jackson {
  deserialization {
    useBigIntegerForInts = true
    useBigDecimalForFloats = true
  }
}
{
  jackson {
    deserialization {
      useBigIntegerForInts = true
      useBigDecimalForFloats = true
    }
  }
}
{
  "jackson": {
    "deserialization": {
      "useBigIntegerForInts": true,
      "useBigDecimalForFloats": true
    }
  }
}


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号