Swift 객체를 JSON으로 직렬화하거나 변환하는 방법은 무엇입니까?


84

이 아래 수업

class User: NSManagedObject {
  @NSManaged var id: Int
  @NSManaged var name: String
}

변환해야합니다.

{
    "id" : 98,
    "name" : "Jon Doe"
}

변수를 사전에 설정하고 사전을 반환하는 함수에 객체를 수동으로 전달하려고했습니다. 그러나 나는 이것을 달성하는 더 나은 방법을 원할 것입니다.


1
가장 좋은 방법은 그것을 실행하고 배열과 사전에 저장 한 다음 변환하는 것입니다.
Schemetrical 2015


@Schemetrical 예를 들어 줄 수 있습니까? 나는 물체를 통과하는 방법을 모른다.
Penkey Suresh 2015

글쎄, 당신은 객체 자체를 살펴보고 모든 값을 가져 와서 수동으로 dict에 추가하고 반복해야합니다.
Schemetrical

@Schemetrical 실제로 시도했습니다. 그러나 큰 개체의 경우 컴파일 시간이 크게 늘어납니다.
Penkey Suresh 2015

답변:


130

Swift 4에서는 Codable유형 에서 상속 할 수 있습니다 .

struct Dog: Codable {
    var name: String
    var owner: String
}

// Encode
let dog = Dog(name: "Rex", owner: "Etgar")

let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(dog)
let json = String(data: jsonData, encoding: String.Encoding.utf16)

// Decode
let jsonDecoder = JSONDecoder()
let secondDog = try jsonDecoder.decode(Dog.self, from: jsonData)

26
인코딩 유형은 .utf16 대신 .utf8해야한다
찬 징 홍콩

2
@ChanJingHong 그것은 당신이 인코딩하려는 것에 달려 있습니다
Etgar

이 특정 예제에서 .utf16이 작동합니까? 시도했지만 작동하지 않습니다.
Chan Jing Hong

@ChanJingHong Strange, 3 개월 전에 시도했는데 효과가있었습니다. 디코딩 방법의 인수로 인코딩 유형도 넣어야한다고 생각합니다. 제가 그것을 확인해 보겠습니다.
Etgar

2
질문은 특히 인코딩 class(내가 필요한 것)이 아닙니다 struct.
곤도

39

이제 Swift 4 (Foundation)와 함께 기본적으로 두 가지 방식으로 지원됩니다. JSON 문자열에서 객체로, 객체에서 JSON 문자열로. 여기에서 Apple의 설명서를 참조하십시오. JSONDecoder () 및 여기 JSONEncoder ()

객체에 대한 JSON 문자열

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let myStruct = try! decoder.decode(myStruct.self, from: jsonData)

JSONString에 대한 Swift 객체

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(myStruct)
print(String(data: data, encoding: .utf8)!)

여기에서 모든 세부 정보와 예제를 찾을 수 있습니다. Swift 4를 사용한 JSON 구문 분석에 대한 궁극적 인 가이드


25

업데이트 : Codable Swift 4에 도입 된 프로토콜은 대부분의 JSON구문 분석 사례에 충분합니다 . 아래 답변은 이전 버전의 Swift와 레거시 이유로 인해 갇혀있는 사람들을위한 것입니다.

EVReflection :

  • 이것은 반사 원리의 작품입니다. 이것은 또한 적은 코드를 받아 지원 NSDictionary, NSCoding, Printable, HashableEquatable

예:

    class User: EVObject { # extend EVObject method for the class
       var id: Int = 0
       var name: String = ""
       var friends: [User]? = []
    }

    # use like below
    let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
    let user = User(json: json)

ObjectMapper :

  • 또 다른 방법은 ObjectMapper를 사용하는 것입니다. 이것은 더 많은 제어를 제공하지만 더 많은 코드를 필요로합니다.

예:

    class User: Mappable { # extend Mappable method for the class
       var id: Int?
       var name: String?

       required init?(_ map: Map) {

       }

       func mapping(map: Map) { # write mapping code
          name    <- map["name"]
          id      <- map["id"]
       }

    }

    # use like below
    let json:String = "{\"id\": 24, \"name\": \"Bob Jefferson\", \"friends\": [{\"id\": 29, \"name\": \"Jen Jackson\"}]}"
    let user = Mapper<User>().map(json)

매핑 이미지도 지원합니까 ??
Charlie

1
@Charlie 미안하지만 그게 가능한지 잘 모르겠습니다.
Penkey Suresh 2015

1
@ Suresh : 표시된 예제와 같이 사용자 지정 변환을 작성하여 가능합니다. 이미지를 String으로 변환 한 다음 Json 객체에 추가했습니다. 그것은 많은 특별히 시계 OS와 함께 작동하는 데 도움이
찰리

1
안녕하세요, Mappable 클래스를 초기화하고 속성을 수동으로 설정 한 다음 객체를 jsonString으로 변환하는 방법을 알고 있습니까?
VAAA

Swift 4 / ios 11에서 코딩 가능한 프로토콜의 영향은 무엇입니까 ?? . 이것을 사용하여 신속한 4에서 NSManagedObject를 JSON으로 변환 할 수 있습니까 ??
user1828845

13

상속이 필요하지 않은 더 작은 솔루션에 대해 약간 작업했습니다. 그러나 그것은 많이 테스트되지 않았습니다. 꽤 못생긴 ATM입니다.

https://github.com/peheje/JsonSerializerSwift

그것을 테스트하기 위해 놀이터로 전달할 수 있습니다. 예 : 다음 클래스 구조 :

//Test nonsense data
class Nutrient {
    var name = "VitaminD"
    var amountUg = 4.2

    var intArray = [1, 5, 9]
    var stringArray = ["nutrients", "are", "important"]
}

class Fruit {
    var name: String = "Apple"
    var color: String? = nil
    var weight: Double = 2.1
    var diameter: Float = 4.3
    var radius: Double? = nil
    var isDelicious: Bool = true
    var isRound: Bool? = nil
    var nullString: String? = nil
    var date = NSDate()

    var optionalIntArray: Array<Int?> = [1, 5, 3, 4, nil, 6]
    var doubleArray: Array<Double?> = [nil, 2.2, 3.3, 4.4]
    var stringArray: Array<String> = ["one", "two", "three", "four"]
    var optionalArray: Array<Int> = [2, 4, 1]

    var nutrient = Nutrient()
}

var fruit = Fruit()
var json = JSONSerializer.toJson(fruit)

print(json)

인쇄물

{"name": "Apple", "color": null, "weight": 2.1, "diameter": 4.3, "radius": null, "isDelicious": true, "isRound": null, "nullString": null, "date": "2015-06-19 22:39:20 +0000", "optionalIntArray": [1, 5, 3, 4, null, 6], "doubleArray": [null, 2.2, 3.3, 4.4], "stringArray": ["one", "two", "three", "four"], "optionalArray": [2, 4, 1], "nutrient": {"name": "VitaminD", "amountUg": 4.2, "intArray": [1, 5, 9], "stringArray": ["nutrients", "are", "important"]}}

신속한 2.3 버전을 제공해 주시겠습니까?
H Raval

8

이것은 완벽한 / 자동 솔루션은 아니지만 이것이 관용적이고 고유 한 방법 이라고 생각합니다 . 이렇게하면 라이브러리 등이 필요하지 않습니다.

다음과 같은 프로토콜을 만듭니다.

/// A generic protocol for creating objects which can be converted to JSON
protocol JSONSerializable {
    private var dict: [String: Any] { get }
}

extension JSONSerializable {
    /// Converts a JSONSerializable conforming class to a JSON object.
    func json() rethrows -> Data {
        try JSONSerialization.data(withJSONObject: self.dict, options: nil)
    }
}

그런 다음 다음과 같이 클래스에서 구현하십시오.

class User: JSONSerializable {
    var id: Int
    var name: String

    var dict { return ["id": self.id, "name": self.name]  }
}

지금:

let user = User(...)
let json = user.json()

참고 : json문자열로 원하는 경우 문자열로 변환하는 것은 매우 간단합니다.String(data: json, encoding .utf8)


6

위의 답변 중 일부는 완전히 괜찮지 만 훨씬 더 읽기 쉽고 사용 가능하도록 여기에 확장 기능을 추가했습니다.

extension Encodable {
    var convertToString: String? {
        let jsonEncoder = JSONEncoder()
        jsonEncoder.outputFormatting = .prettyPrinted
        do {
            let jsonData = try jsonEncoder.encode(self)
            return String(data: jsonData, encoding: .utf8)
        } catch {
            return nil
        }
    }
}

struct User: Codable {
     var id: Int
     var name: String
}

let user = User(id: 1, name: "name")
print(user.convertToString!)

// 다음과 같이 인쇄됩니다.

{
  "id" : 1,
  "name" : "name"
}

2

lib / framework가 있는지 확실하지 않지만 자동으로 수행하고 수동 작업을 피하고 싶다면 :-) stick with MirrorType...

class U {

  var id: Int
  var name: String

  init(id: Int, name: String) {
    self.id = id
    self.name = name
  }

}

extension U {

  func JSONDictionary() -> Dictionary<String, Any> {
    var dict = Dictionary<String, Any>()

    let mirror = reflect(self)

    var i: Int
    for i = 0 ; i < mirror.count ; i++ {
      let (childName, childMirror) = mirror[i]

      // Just an example how to check type
      if childMirror.valueType is String.Type {
        dict[childName] = childMirror.value
      } else if childMirror.valueType is Int.Type {
        // Convert to NSNumber for example
        dict[childName] = childMirror.value
      }
    }

    return dict
  }

}

대략적인 예를 들어, 적절한 변환 지원이 부족하고, 재귀가 부족합니다. ... 단지 MirrorType데모 일뿐입니다 .

추신 여기에서는에서 완료 U되었지만 향상시키고 NSManagedObject모든 NSManagedObject하위 클래스 를 변환 할 수 있습니다 . 모든 서브 클래스 / 관리 객체에서이를 구현할 필요가 없습니다.


안녕, 나는이 방법을 시도하고 있지만 매번 0을 카운트로 얻습니다. 내가해야 할 일에 대해 좀 더 구체적으로 말씀해 주시겠습니까? @NSManaged가 문제를 일으키는 것 같습니다. NSManagedObject를 어떻게 향상시킬 수 있습니까?
Penkey 수레 쉬

시도하지 않았다 @NSManaged . 아마도 그것은 당신의 문제를 일으키는 것입니다. 이 경우 Objective-C로 작성한 다음 Swift에서 사용합니다.
zrzka 2015

0

2020 | SWIFT 5.1 :

(SWIFT 4에서도 작동)


작업 준비 완료!

용법:

var msgTemplates = [msgTemlate]()

// load from file
msgTemplates = try! Serializer.load(from: url)!

// save to file
Serializer.save(data: msgTemplates, to: url)

다음 코드는 세 가지를 해결합니다.

  • 파일을 저장할 문자열 1 개
  • 파일을로드 할 문자열 1 개
  • 일부 Codable 요소의 JSON 가져 오기 / 인쇄 기능 element.toJsonString
    import Foundation

    public class Serializer{
        static func save<T>(data: T, to url: URL) where T : Encodable{
            guard let json = data.toJsonString else { return }

            do {
                try json.write(to: url, atomically: true, encoding: String.Encoding.utf8)
            }
            catch { /* error handling here */ }
        }

        static func load<T>(from url: URL) throws -> T? where T : Decodable {
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601 // for human-read date format

            guard let dataStr = try? String(contentsOf: url, encoding: String.Encoding.utf8 ),
                  let data = dataStr.data(using: String.Encoding.utf8 ),
                  let result = try? decoder.decode( T.self , from: data)
            else { return nil }

            return result
        }
    }

    extension Encodable {
        var toJsonString: String? {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted  // nice formatted for reading by human
            encoder.dateEncodingStrategy = .iso8601    // for human-read date format

            do {
                let jsonData = try encoder.encode(self)
                return String(data: jsonData, encoding: .utf8)
            } catch {
                return nil
            }
        }
    }

PS : 및 ofc 데이터는 코드화 가능해야합니다.

struct msgTemlate: Codable { 
     //some params
}

PS2 : msgTemlate에 열거 형이있는 경우에도 코딩 가능해야합니다.


0
struct User:Codable{
 var id:String?
 var name:String?
 init(_ id:String,_ name:String){
   self.id  = id
   self.name = name
 }
}

이제 당신의 물건을 이렇게 만드십시오.

let user = User ( "1", "pawan")

do{
      let userJson =  try JSONEncoder().encode(parentMessage) 
            
    }catch{
         fatalError("Unable To Convert in Json")      
    }

그런 다음 json에서 Object로 다시 변환하십시오.

let jsonDecoder = JSONDecoder()
do{
   let convertedUser = try jsonDecoder.decode(User.self, from: userJson.data(using: .utf8)!)
 }catch{
   
 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.