JSON 写入崩溃中的另一个无效顶级类型

Another Invalid top-level type in JSON write crash

所以,Xcode 13.1,iOS 15.0,我坚持这个 JSON 序列化一个相当重要的项目,它必须像往常一样在昨天(!)交付,并且而是崩溃。

这是我正在处理的代码片段:

var jsonData: Data
    do {
        jsonData = try JSONSerialization.data(withJSONObject: roomBookings, options: [])
    } catch {
        print("error: ", error)
        return false
    }

这是我要JSON序列化的对象:

Rooms.RoomBookings(
bookings: [Rooms.RoomBooking(
bookingID: "23EB86CB-A918-47D4-ADDB-346DBB4E3471",
 roomID: "BgX86SbN0UifijkwU8HZ", 
isAllDay: false, 
startTimestamp: 1636440856861, 
endTimestamp: 1636444456861, 
user: "trialUser")
])

这是我不断收到的错误,它使应用程序崩溃(当然我们可以避免使用 isValidJSONObject 进行实际的崩溃检查,但这不是这里的重点)。

Terminating app due to uncaught exception 'NSInvalidArgumentException',
 reason: '*** +[NSJSONSerialization dataWithJSONObject:options:error:]:
 Invalid top-level type in JSON write'

下面是模型,错误集中在顶层类型,这是怎么回事?它只是 ArrayRoooBooking 实例,符合 Codable,那又怎样?

有人知道我做错了什么吗?将不胜感激!

// MARK: - RoomBookings
struct RoomBookings: Codable {
    var bookings: [RoomBooking]

    enum CodingKeys: String, CodingKey {
        case bookings = "bookings"
    }
}

// MARK: RoomBookings convenience initializers and mutators
extension RoomBookings {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(RoomBookings.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        bookings: [RoomBooking]? = nil
    ) -> RoomBookings {
        return RoomBookings(
            bookings: bookings ?? self.bookings
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

// MARK: - RoomBooking
struct RoomBooking: Codable {
    var bookingID: String
    var roomID: String
    var isAllDay: Bool
    var startTimestamp: Int
    var endTimestamp: Int
    var user: String

    enum CodingKeys: String, CodingKey {
        case bookingID = "bookingId"
        case roomID = "roomId"
        case isAllDay = "isAllDay"
        case startTimestamp = "startTimestamp"
        case endTimestamp = "endTimestamp"
        case user = "user"
    }
}

// MARK: RoomBooking convenience initializers and mutators

extension RoomBooking {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(RoomBooking.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        bookingID: String? = nil,
        roomID: String? = nil,
        isAllDay: Bool? = nil,
        startTimestamp: Int? = nil,
        endTimestamp: Int? = nil,
        user: String? = nil
    ) -> RoomBooking {
        return RoomBooking(
            bookingID: bookingID ?? self.bookingID,
            roomID: roomID ?? self.roomID,
            isAllDay: isAllDay ?? self.isAllDay,
            startTimestamp: startTimestamp ?? self.startTimestamp,
            endTimestamp: endTimestamp ?? self.endTimestamp,
            user: user ?? self.user
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

// MARK: - Helper functions for creating encoders and decoders

func newJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        decoder.dateDecodingStrategy = .iso8601
    }
    return decoder
}

func newJSONEncoder() -> JSONEncoder {
    let encoder = JSONEncoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        encoder.dateEncodingStrategy = .iso8601
    }
    return encoder
}

尝试

    var jsonData: Data
    do {
        jsonData = try JSONEncoder().encode(roomBookings)
        print(String(data: data, encoding: .utf8)!) //to check the actual O/P am adding it here, remove it from your code
    } catch {
        print("error: ", error)
        return false
    }

O/P:

{ "bookings": [{ "endTimestamp": 1, "roomId": "1", "user": "abcd", "isAllDay": true, "bookingId": "1", "startTimestamp": 1 }] }