So, Xcode 13.1, iOS 15.0, I am stuck with this JSON serialization for a rather important project which has to be delivered yesterday(!) as usual, and instead crashes.
This is the piece of code I'm dealing with:
var jsonData: Data
do {
jsonData = try JSONSerialization.data(withJSONObject: roomBookings, options: [])
} catch {
print("error: ", error)
return false
}
This is the object I am trying to JSON serialize:
Rooms.RoomBookings(
bookings: [Rooms.RoomBooking(
bookingID: "23EB86CB-A918-47D4-ADDB-346DBB4E3471",
roomID: "BgX86SbN0UifijkwU8HZ",
isAllDay: false,
startTimestamp: 1636440856861,
endTimestamp: 1636444456861,
user: "trialUser")
])
This is the error I keep getting, which makes the app crash (of course we can avoid the actual crash checking with isValidJSONObject, but that's not the point here).
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** +[NSJSONSerialization dataWithJSONObject:options:error:]:
Invalid top-level type in JSON write'
Here below is the model, The error is focused on the top-level type, what's wrong with that? It's just an Array of RoooBooking instances, conformed with Codable, so what?
Someone knows what am I doing wrong? That would be greatly appreciated!
// 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
}
Try
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 }] }