英文:
Generated OpenAPI golang client doesn't seem to handle dates correctly?
问题
我正在开发一个提供交易列表的API。我正在使用Java/Kotlin Spring编写它,但是我更喜欢使用golang来编写命令行界面,所以我正在为它生成一个golang客户端。该API在Swagger UI中运行良好。
Kotlin API:
@GetMapping
fun listTransactions() : ResponseEntity<List<Transaction>> {
val transactions = ArrayList<Transaction>()
transactionRepository.findAll().mapTo(transactions) { fromEntity(it) }
return ResponseEntity.ok(transactions)
}
Kotlin对象:
data class Transaction(
val id: Long,
val transactionDate: Date, // Java SQL date
val postedDate: Date?, // Java SQL date
val amount: BigDecimal,
val category: Category,
val merchant: Merchant,
val merchantDescription: String?
)
生成的Schema:
{
"openapi": "3.0.1",
"info": {
"title": "BFI Swagger Title",
"description": "BFI description",
"version": "0.1"
},
"servers": [{
"url": "http://localhost:8080",
"description": "Generated server url"
}],
"paths": {
"/transaction": {
"get": {
"tags": ["transaction-router"],
"operationId": "listTransactions",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Transaction"
}
}
}
}
}
}
},
"post": {
"tags": ["transaction-router"],
"operationId": "createTransaction",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTransactionRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/Transaction"
}
}
}
}
}
}
},
"/hello": {
"get": {
"tags": ["category-router"],
"summary": "Hello there!",
"operationId": "hello",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Category"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"CreateTransactionRequest": {
"type": "object",
"properties": {
"transactionDate": {
"type": "string",
"format": "date-time"
},
"postedDate": {
"type": "string",
"format": "date-time"
},
"amount": {
"type": "number"
},
"categoryId": {
"type": "integer",
"format": "int64"
},
"merchantId": {
"type": "integer",
"format": "int64"
},
"merchantDescription": {
"type": "string"
}
}
},
"Category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"Merchant": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"Transaction": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"transactionDate": {
"type": "string",
"format": "date-time"
},
"postedDate": {
"type": "string",
"format": "date-time"
},
"amount": {
"type": "number"
},
"category": {
"$ref": "#/components/schemas/Category"
},
"merchant": {
"$ref": "#/components/schemas/Merchant"
},
"merchantDescription": {
"type": "string"
}
}
}
}
}
}
Golang客户端对象:
type Transaction struct {
Id *int64 `json:"id,omitempty"`
TransactionDate *time.Time `json:"transactionDate,omitempty"`
PostedDate *time.Time `json:"postedDate,omitempty"`
Amount *float32 `json:"amount,omitempty"`
Category *Category `json:"category,omitempty"`
Merchant *Merchant `json:"merchant,omitempty"`
MerchantDescription *string `json:"merchantDescription,omitempty"`
}
所有这些看起来都是正确的。然而,当我使用OpenAPI客户端时,似乎反序列化不正确:
Error when calling `TransactionRouterApi.ListTransactions`: parsing time "\"2022-10-28\"" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "\"\"" as "T"
Full HTTP response: &{200 200 HTTP/1.1 1 1 map[Content-Type:[application/json] Date:[Sat, 29 Oct 2022 04:03:31 GMT]] {[{"id":1,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null},{"id":2,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null},{"id":3,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null}]} -1 [chunked] false false map[] 0x140001daf00 <nil>}
Response from `TransactionRouterApi.ListTransactions`: [{0x140000aa218 0001-01-01 00:00:00 +0000 UTC <nil> <nil> <nil> <nil> <nil>}]
我是否做错了什么导致反序列化失败?还是这是一个客户端的错误(似乎不太可能,但谁知道呢)。
我已经查看了我使用的生成参数和我的端点上可用的模式,它们都看起来是正确的。
执行的脚本:openapi-generator-cli generate -g go -i http://localhost:8080/v3/api-docs
英文:
I'm working on an API that provides a list of transactions. I'm writing it in Java/Kotlin Spring, but prefer golang for CLIs, so I'm generating a golang client for it. The API works well in the Swagger UI.
Kotlin API:
@GetMapping
fun listTransactions() : ResponseEntity<List<Transaction>> {
val transactions = ArrayList<Transaction>()
transactionRepository.findAll().mapTo(transactions) { fromEntity(it) }
return ResponseEntity.ok(transactions)
}
Kotlin Object:
data class Transaction(
val id: Long,
val transactionDate: Date, // Java SQL date
val postedDate: Date?, // Java SQL date
val amount: BigDecimal,
val category: Category,
val merchant: Merchant,
val merchantDescription: String?
)
Generated Schema:
{
"openapi": "3.0.1",
"info": {
"title": "BFI Swagger Title",
"description": "BFI description",
"version": "0.1"
},
"servers": [{
"url": "http://localhost:8080",
"description": "Generated server url"
}],
"paths": {
"/transaction": {
"get": {
"tags": ["transaction-router"],
"operationId": "listTransactions",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Transaction"
}
}
}
}
}
}
},
"post": {
"tags": ["transaction-router"],
"operationId": "createTransaction",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTransactionRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/Transaction"
}
}
}
}
}
}
},
"/hello": {
"get": {
"tags": ["category-router"],
"summary": "Hello there!",
"operationId": "hello",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Category"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"CreateTransactionRequest": {
"type": "object",
"properties": {
"transactionDate": {
"type": "string",
"format": "date-time"
},
"postedDate": {
"type": "string",
"format": "date-time"
},
"amount": {
"type": "number"
},
"categoryId": {
"type": "integer",
"format": "int64"
},
"merchantId": {
"type": "integer",
"format": "int64"
},
"merchantDescription": {
"type": "string"
}
}
},
"Category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"Merchant": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"Transaction": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"transactionDate": {
"type": "string",
"format": "date-time"
},
"postedDate": {
"type": "string",
"format": "date-time"
},
"amount": {
"type": "number"
},
"category": {
"$ref": "#/components/schemas/Category"
},
"merchant": {
"$ref": "#/components/schemas/Merchant"
},
"merchantDescription": {
"type": "string"
}
}
}
}
}
}
Golang Client Object:
type Transaction struct {
Id *int64 `json:"id,omitempty"`
TransactionDate *time.Time `json:"transactionDate,omitempty"`
PostedDate *time.Time `json:"postedDate,omitempty"`
Amount *float32 `json:"amount,omitempty"`
Category *Category `json:"category,omitempty"`
Merchant *Merchant `json:"merchant,omitempty"`
MerchantDescription *string `json:"merchantDescription,omitempty"`
}
All of this seems correct enough. However, when I use the OpenAPI client, it seems like deserialization isn't working correctly:
Error when calling `TransactionRouterApi.ListTransactions``: parsing time "\"2022-10-28\"" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "\"" as "T"
Full HTTP response: &{200 200 HTTP/1.1 1 1 map[Content-Type:[application/json] Date:[Sat, 29 Oct 2022 04:03:31 GMT]] {[{"id":1,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null},{"id":2,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null},{"id":3,"transactionDate":"2022-10-28","postedDate":"2022-10-28","amount":0.00,"category":{"id":1,"name":"test","note":"test"},"merchant":{"id":1,"name":"test","note":"test"},"merchantDescription":null}]} -1 [chunked] false false map[] 0x140001daf00 <nil>}
Response from `TransactionRouterApi.ListTransactions`: [{0x140000aa218 0001-01-01 00:00:00 +0000 UTC <nil> <nil> <nil> <nil> <nil>}]
Am I doing something incorrectly that results in deserialization failing? Or is this a client bug (seems doubtful, but who knows).
I've looked at the generation arguments I used and the schema available at my endpoint, and they both appear correct.
Script executed: openapi-generator-cli generate -g go -i http://localhost:8080/v3/api-docs
答案1
得分: 1
两个选项:
- 创建一个自定义类型,并为日期字段实现
Unmarshaler
接口。 - 从API返回有效的RFC 3339日期/时间。
英文:
Two options:
- Create a custom type and implement the
Unmarshaler
interface for the date fields. - Return valid RFC 3339 date/times from the API.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论