Micronaut http 客户端无法识别字段

Micronaut http client doesn't recognise fields

我从休息服务处收到以下 json。

{
  "AccessControlRecords": {
    "OccasionRecords": [
      {
        "Type": "",
        "BookingNr": "91376",
        "BookingRow": "1",
        "FacilityID": "OIHAL",
        "ObjectID": "STYRK",
        "PartOfObjectId": "",
        "PartOfObjectName": "",
        "FacilityName": "Odense Idrætshal",
        "ObjectName": "Styrketræningslokale",
        "BookFromDate": "17.08.2019",
        "BookToDate": "23.05.2020",
        "OccasionDate": "2020-01-04 09:00",
        "OccasionToDate": "2020-01-04 10:00",
        "FomKlo": "09:00",
        "TomKlo": "10:00",
        "TimeBefore": "0",
        "TimeAfter": "0",
        "Weekday": 0,
        "BookStatus": "",
        "KortReg": "",
        "CustomerID": "",
        "CustomerName": "Svømmeklubben Frem",
        "TypeOfBooking": "E",
        "CreatedBy": "",
        "Cards": null,
        "FunctionCode1": 0,
        "FunctionCode2": 0,
        "FunctionCode3": 0,
        "FunctionCode4": "",
        "Text1": "",
        "Text2": "",
        "Text3": "",
        "Text4": "",
        "EndUser": "",
        "Activity": "Styrketræning"
      },
      {
        "Type": "",
        "BookingNr": "90443",
        "BookingRow": "1",
        "FacilityID": "OIHAL",
        "ObjectID": "STYRK",
        "PartOfObjectId": "",
        "PartOfObjectName": "",
        "FacilityName": "Odense Idrætshal",
        "ObjectName": "Styrketræningslokale",
        "BookFromDate": "02.11.2019",
        "BookToDate": "28.03.2020",
        "OccasionDate": "2020-01-04 14:00",
        "OccasionToDate": "2020-01-04 16:30",
        "FomKlo": "14:00",
        "TomKlo": "16:30",
        "TimeBefore": "0",
        "TimeAfter": "0",
        "Weekday": 1,
        "BookStatus": "",
        "KortReg": "",
        "CustomerID": "",
        "CustomerName": "Odense Roklub",
        "TypeOfBooking": "E",
        "CreatedBy": "",
        "Cards": null,
        "FunctionCode1": 0,
        "FunctionCode2": 0,
        "FunctionCode3": 0,
        "FunctionCode4": "",
        "Text1": "",
        "Text2": "",
        "Text3": "",
        "Text4": "",
        "EndUser": "",
        "Activity": "Styrketræning"
      },
      {
        "Type": "",
        "BookingNr": "91916",
        "BookingRow": "1",
        "FacilityID": "OIHAL",
        "ObjectID": "STYRK",
        "PartOfObjectId": "DELA",
        "PartOfObjectName": "Delt styrketræning (A)",
        "FacilityName": "Odense Idrætshal",
        "ObjectName": "Styrketræningslokale",
        "BookFromDate": "24.08.2019",
        "BookToDate": "20.06.2020",
        "OccasionDate": "2020-01-04 10:00",
        "OccasionToDate": "2020-01-04 14:00",
        "FomKlo": "10:00",
        "TomKlo": "14:00",
        "TimeBefore": "0",
        "TimeAfter": "0",
        "Weekday": 0,
        "BookStatus": "",
        "KortReg": "",
        "CustomerID": "",
        "CustomerName": "* Odense Håndbold talentcenter",
        "TypeOfBooking": "E",
        "CreatedBy": "",
        "Cards": null,
        "FunctionCode1": 0,
        "FunctionCode2": 0,
        "FunctionCode3": 0,
        "FunctionCode4": "",
        "Text1": "",
        "Text2": "",
        "Text3": "",
        "Text4": "",
        "EndUser": "",
        "Activity": "Styrketræning"
      }
    ]
  },
  "ResultCode": 0,
  "ResultText": null
}

我的数据class定义如下

data class Bookings(val AccessControlRecords: AccessControlRecords, val resultCode: Int, val resultText: String?)

data class AccessControlRecords(val OccasionRecords: List<OccasionRecords>)

data class OccasionRecords(val type: String?,
                           val BookingNr: String,
                           val bookingRow: String,
                           val FacilityID: String,
                           val ObjectID: String,
                           val PartOfObjectId: String,
                           val PartOfObjectName: String,
                           val FacilityName: String,
                           val ObjectName: String,
                           val BookFromDate: String,
                           val BookToDate: String,
                           val OccasionDate: String,
                           val OccasionToDate: String,
                           val FomKlo: String,
                           val TomKlo: String,
                           val TimeBefore: String,
                           val TimeAfter: String,
                           val Weekday: Int,
                           val BookStatus: String,
                           val KortReg: String,
                           val CustomerID: String,
                           val CustomerName: String,
                           val TypeOfBooking: String,
                           val CreatedBy: String,
                           val Cards: String,
                           val FunctionCode1: Int,
                           val FunctionCode2: Int,
                           val FunctionCode3: Int,
                           val FunctionCode4: String,
                           val Text1: String,
                           val Text2: String,
                           val Text3: String,
                           val Text4: String,
                           val EndUser: String,
                           val Activity: String)

我的配置class定义如下

class BookingConfiguration {
    companion object {
        const val URL = "https://something.dk"
        const val PATH = "/kmd_webapi/api/Monitor/GetFilteredAccessControlRecords?dateTimeFrom=2020-01-04&dateTimeTo=2020-01-04&facility=OIHAL&facilityObject=STYRK&partObject=&authenticationCode=xxx&type=json"
    }
}

实际客户端定义如下

@Client(BookingConfiguration.URL)
interface BookingsClient {
    @Get(BookingConfiguration.PATH)
    fun fetchBookings(dateTimeFrom: String, dateTimeTo: String, authenticationCode: String): Flowable<Bookings>
}

我遇到的异常如下

01:31:59.199 [pool-1-thread-3] ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: Error decoding JSON stream for type [T]: Instantiation of [simple type, class dk.fitfit.OccasionRecords] value failed for JSON property BookingNr due to missing (therefore NULL) value for creator parameter BookingNr which is a non-nullable type
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: dk.fitfit.Bookings["AccessControlRecords"]->java.lang.Object[0]->dk.fitfit.OccasionRecords["BookingNr"])
io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [T]: Instantiation of [simple type, class dk.fitfit.OccasionRecords] value failed for JSON property BookingNr due to missing (therefore NULL) value for creator parameter BookingNr which is a non-nullable type
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: dk.fitfit.Bookings["AccessControlRecords"]->java.lang.Object[0]->dk.fitfit.OccasionRecords["BookingNr"])
    at io.micronaut.jackson.codec.JsonMediaTypeCodec.decode(JsonMediaTypeCodec.java:123)
    at io.micronaut.http.client.DefaultHttpClient.lambda$null(DefaultHttpClient.java:905)
    at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:63)
    at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext[=14=](InstrumentedSubscriber.java:80)
    at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
...

我注意到 属性 Bookings.AccessControlRecords 必须是大写的。但是 Bookings.resultCode 和 Bookings.resultText 并没有,我觉得很奇怪。与 AccessControlRecords.OccasionRecords 相同。当然,我一直在为 BookingNr 属性 尝试不同的案例,但无济于事。

谁能告诉我我做错了什么?或者也许有人知道如何进一步调试它?

如下所示使用@JsonProperty 解决了问题。

data class Bookings(@JsonProperty("AccessControlRecords") val accessControlRecords: AccessControlRecords, val resultCode: Int, val resultText: String?)