Làm cách nào để tuần tự hóa hoặc chuyển đổi các đối tượng Swift sang JSON?


81

Lớp dưới này

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

Cần được chuyển đổi thành

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

Tôi đã thử chuyển đối tượng theo cách thủ công tới một hàm đặt các biến vào từ điển và trả về từ điển. Nhưng tôi muốn có một cách tốt hơn để thực hiện điều này.


1
Đặt cược tốt nhất của bạn là chạy qua nó và lưu nó vào các mảng và mảng, sau đó chuyển đổi.
Schemetrical

Tham khảo cái này - github.com/dankogai/swift-json
Uttam Sinha

@Schemetrical bạn có thể chỉ cho tôi một ví dụ được không? Tôi không biết làm thế nào để chạy qua một đối tượng.
Penkey Suresh

Vâng, bạn cần phải đi qua chính đối tượng, lấy tất cả các giá trị và thêm nó vào một chính tả theo cách thủ công, và lặp lại.
Schemetrical

@Schemetrical Tôi thực sự đã thử điều đó. Nhưng đối với các đối tượng lớn thời gian biên dịch tăng lên đáng kể.
Penkey Suresh

Câu trả lời:


126

Trong Swift 4, bạn có thể kế thừa từ Codablekiểu.

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)

21
Các loại mã hóa nên .utf8 thay vì .utf16
Chan Jing Hồng

2
@ChanJingHong nó phụ thuộc vào những gì bạn đang cố gắng mã hóa
Etgar

Đối với ví dụ cụ thể này .utf16 có hoạt động không? Tôi đã thử nhưng nó không hoạt động.
Chan Jing Hong

@ChanJingHong Thật kỳ lạ, tôi đã thử nó 3 tháng trước và nó hoạt động. Tôi nghĩ rằng tôi nên đặt kiểu mã hóa cũng như một đối số trong phương pháp giải mã. Tôi sẽ kiểm tra.
Etgar

2
câu hỏi đặc biệt về mã hóa class(những gì tôi cần) không struct.
gondo

37

Cùng với Swift 4 (Foundation), giờ đây nó được hỗ trợ nguyên bản theo cả hai cách, chuỗi JSON thành một đối tượng - một đối tượng đối với chuỗi JSON. Vui lòng xem tài liệu của Apple tại đây JSONDecoder () và tại đây JSONEncoder ()

Chuỗi JSON đến đối tượng

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

Đối tượng Swift đối với JSONString

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

Bạn có thể tìm thấy tất cả các chi tiết và ví dụ tại đây Hướng dẫn cơ bản để phân tích cú pháp JSON với Swift 4


25

CẬP NHẬT: Codable giao thức được giới thiệu trong Swift 4 sẽ đủ cho hầu hết các JSONtrường hợp phân tích cú pháp. Câu trả lời dưới đây dành cho những người gặp khó khăn trong các phiên bản Swift trước và vì lý do cũ

EVReflection :

  • Điều này hoạt động của nguyên tắc phản ánh. Này có mã ít hơn và cũng hỗ trợ NSDictionary, NSCoding, Printable, HashableEquatable

Thí dụ:

    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 :

  • Một cách khác là sử dụng ObjectMapper. Điều này mang lại nhiều quyền kiểm soát hơn nhưng cũng mất nhiều mã hơn.

Thí dụ:

    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)

Nó cũng hỗ trợ ánh xạ hình ảnh ??
Charlie

1
@Charlie xin lỗi, tôi không chắc điều đó có thể không.
Penkey Suresh

1
@ Suresh: Có thể bằng cách viết biến đổi tùy chỉnh như các ví dụ minh họa. Tôi đã chuyển đổi hình ảnh thành Chuỗi và sau đó thêm vào đối tượng Json. Nó sẽ giúp rất nhiều đặc biệt làm việc với os đồng hồ
Charlie

1
Xin chào, bạn có biết cách khởi tạo một lớp Mappable và đặt thuộc tính theo cách thủ công rồi chuyển đổi đối tượng thành jsonString không?
VAAA

tác động của giao thức codable từ swift 4 / ios 11 là gì ?? . Chúng tôi có thể chuyển đổi NSManagedObject sang JSON trong swift 4 bằng cách sử dụng cái này không ??
user1828845

13

Tôi đã làm việc một chút trên một giải pháp nhỏ hơn không yêu cầu kế thừa. Nhưng nó đã không được thử nghiệm nhiều. Nó khá xấu atm.

https://github.com/peheje/JsonSerializerSwift

Bạn có thể chuyển nó vào một sân chơi để kiểm tra nó. Ví dụ: cấu trúc lớp sau:

//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)

bản in

{"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"]}}

bạn có thể vui lòng cung cấp phiên bản 2.3 nhanh chóng của nó không?
H Raval

8

Đây không phải là một giải pháp hoàn hảo / tự động nhưng tôi tin rằng đây là cách thành ngữ và bản địa để làm như vậy. Bằng cách này, bạn không cần bất kỳ thư viện nào hoặc như vậy.

Tạo một giao thức như:

/// 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)
    }
}

Sau đó triển khai nó trong lớp của bạn chẳng hạn như:

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

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

Hiện nay:

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

Lưu ý: nếu bạn muốn ở jsondạng chuỗi, rất đơn giản là chuyển đổi thành chuỗi:String(data: json, encoding .utf8)


6

Một số câu trả lời ở trên là hoàn toàn ổn, nhưng tôi đã thêm một tiện ích mở rộng ở đây, chỉ để làm cho nó dễ đọc và dễ sử dụng hơn.

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!)

// Điều này sẽ in như sau:

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

2

Không chắc liệu lib / framework có tồn tại hay không, nhưng nếu bạn muốn làm nó tự động và bạn muốn tránh lao động thủ công :-) hãy gắn bó với 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
  }

}

Lấy nó làm ví dụ sơ bộ, thiếu hỗ trợ chuyển đổi thích hợp, thiếu đệ quy, ... Nó chỉ là MirrorTypetrình diễn ...

PS Đến đây là xong U, nhưng bạn sẽ nâng cao NSManagedObjectvà sau đó bạn sẽ có thể chuyển đổi tất cả các NSManagedObjectlớp con. Không cần thực hiện điều này trong tất cả các lớp con / đối tượng được quản lý.


Xin chào, tôi đang thử phương pháp này, nhưng lần nào tôi cũng nhận được số 0. Bạn có thể nói rõ hơn về những gì tôi nên làm không? Tôi nghĩ rằng @NSManaged đang gây ra sự cố. Làm cách nào để nâng cao NSManagedObject?
Penkey Suresh

Không thử với @NSManaged. Có thể nó đang gây ra vấn đề của bạn. Trong trường hợp này, tôi sẽ viết nó bằng Objective-C và sau đó sẽ sử dụng nó từ Swift.
zrzka

0

Năm 2020 | SWIFT 5.1:

(cũng hoạt động với SWIFT 4)


Giải pháp sẵn sàng để làm việc!

Sử dụng:

var msgTemplates = [msgTemlate]()

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

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

Đoạn mã sau giải quyết được 3 điều:

  • 1 chuỗi để lưu tệp
  • 1 chuỗi để tải tệp
  • khả năng lấy / in JSON của một số phần tử có thể mã hóa với 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
            }
        }
    }

Dữ liệu PS: và ofc phải có thể mã hóa:

struct msgTemlate: Codable { 
     //some params
}

PS2: trong trường hợp msgTemlate có enums, chúng cũng phải được Codable


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

Bây giờ chỉ cần làm cho đối tượng của bạn như thế này

let user = Người dùng ("1", "pawan")

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

Sau đó chuyển đổi lại từ json sang Object

let jsonDecoder = JSONDecoder()
do{
   let convertedUser = try jsonDecoder.decode(User.self, from: userJson.data(using: .utf8)!)
 }catch{
   
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.