Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 2. 인코딩 개념 - JSONSerialization 사용 시 주의 사항 ([NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON write) 본문

iOS 응용 (swift)

[iOS - swift] 2. 인코딩 개념 - JSONSerialization 사용 시 주의 사항 ([NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON write)

jake-kim 2023. 8. 30. 21:31

1. 인코딩 개념 - JSONSerialization와 JSONEncoder() 차이

2. 인코딩 개념 - JSONSerialization 사용 시 주의 사항 (크래시 - JSON write)

JSONSerialization 개념 (복습)

  • JSONSerialization은 jsonData 타입을 [String: Any]으로 변경하는 것 혹은 그 반대로 변환 ([String: Any] => jsonData)
    • JSONEncoder와 차이 및 구체적인 개념은 이전 포스팅 글 참고
  • 만약 struct을 jsonData로 변환하려면 JSONEncoder를 사용해야함
    • cf) jsonData를 struct로 변환할 경우 JSONDecoder 사용

JSONSerialization 주의 사항1) struct 변환

  • jsonData를 struct로 변환하려면 JSONSerialization을 사용하는것이 아니고, JSONDecoder()를 사용하는 것임을 주의
    • 서버로부터 json String 형태를 받으면 이 형태를 Data로 변경하고 이 Data를 아래처럼 JSONDecoder를 통해 Codable을 준수하는 struct로 변경
struct Person: Codable {
    let name: String
    let age: Int
    let isStudent: Bool
}

// 서버로 부터 json 스트링 내려왔다고 가정
let jsonString = """
{
    "name": "jake",
    "age": 30,
    "isStudent": false
}
"""

// 1. 스트링을 data로 변환
let jsonData = jsonString.data(using: .utf8)!

// 2. data를 struct로 변환
let decoder = JSONDecoder()
let person = try? decoder.decode(Person.self, from: jsonData)
print(person!)
/*
 Person(name: "jake", age: 30, isStudent: false)
*/
  • Swift4 이전에는 Codable키워드가 없었으므로 JSONSerialization를 사용하여 struct로 변환하려면 아래처럼 번거롭게 변환
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
let name = jsonObject["name"] as! String
let age = jsonObject["age"] as! Int
let isStudent = jsonObject["isStudent"] as! Bool

let newPerson = Person(name: name, age: age, isStudent: isStudent)
print(newPerson)
/*
 Person(name: "jake", age: 30, isStudent: false)
*/

JSONSerialization 주의 사항2) 인코딩 시 크래시 발생

  • JSONSerialization으로 인코딩 시 최대 약점은 크래시가 발생하는 케이스가 존재
  • JSONSerialization으로 인코딩 할 때 jsonData를 사용하면 아래처럼 정상 동작이 가능
/// 정상 케이스
let newJsonObject = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
let newJsonData = try! JSONSerialization.data(withJSONObject: newJsonObject)
print(jsonData) // 61 bytes
  • 만약 jsonData를 파라미터로 넘기지 않으면 예외에도 걸리지 않고 바로 크래시가 나므로 휴먼에러 발생 시 위험한 코드
/// 크레시 발생 케이스 (jsonData를 넣지 않은 경우)
let personObject = Person(name: "jake", age: 1, isStudent: false)
if let jsonData3 = try? JSONSerialization.data(withJSONObject: personObject) {
    print("jsonData3>", jsonData3)
} else {
    print("failed, jsonData3")
}

크래시 발생

(크래시가 발생한 부분 - JSONSerialization에서 jsonData가 아닌, 일반 object를 넣은 경우 발생)

정리

  • 디코딩
    • jsonData를 struct로 변환할 때 JSONSerialization, JSONDecoder 둘 다 사용이 가능하지만, JSONDecoder가 가장 간편
  • 인코딩
    • JSONSerialization.data(withJSONObject:) 메서드에 전달되는 객체의 형태가 [String: Any]여야 하는데, [String: Any]가 아니면 바로 크래시가 발생하므로 주의

* 전체 코드: https://github.com/JK0369/ExJSON

Comments