如何将 Swift 对象序列化或转换为 JSON?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29599005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 17:50:46  来源:igfitidea点击:

How to serialize or convert Swift objects to JSON?

jsonswift

提问by Penkey Suresh

This below class

这下面的类

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

Needs to be converted to

需要转换为

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

I tried manually passing the object to a function which sets the variables into a dictionary and returns the dictionary. But I would want a better way to accomplish this.

我尝试手动将对象传递给一个函数,该函数将变量设置为字典并返回字典。但我想要一个更好的方法来实现这一点。

回答by Etgar

In Swift 4, you can inherit from the Codabletype.

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

回答by mohacs

Along with Swift 4 (Foundation) now it is natively supported in both ways, JSON string to an object - an object to JSON string. Please see Apple's documentation here JSONDecoder()and here JSONEncoder()

与 Swift 4(Foundation)一起,现在它以两种方式原生支持,JSON 字符串到对象 - 对象到 JSON 字符串。请在此处查看 Apple 文档JSONDecoder()和此处JSONEncoder()

JSON String to Object

JSON 字符串到对象

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

Swift Object to JSONString

Swift 对象到 JSONString

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

You can find all details and examples here Ultimate Guide to JSON Parsing With Swift 4

您可以在此处找到所有详细信息和示例使用 Swift 4 进行 JSON 解析的终极指南

回答by Penkey Suresh

UPDATE:Codableprotocol introduced in Swift 4 should be sufficient for most of the JSONparsing cases. Below answer is for people who are stuck in previous versions of Swift and for legacy reasons

更新:CodableSwift 4 中引入的协议应该足以满足大多数JSON解析情况。以下答案适用于因遗留原因而被困在以前版本的 Swift 中的人

EVReflection:

EV反射

  • This works of reflection principle. This takes less code and also supports NSDictionary, NSCoding, Printable, Hashableand Equatable
  • 这是反射原理的作品。这需要更少的代码,同时还支持NSDictionaryNSCodingPrintableHashableEquatable

Example:

例子:

    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 Hymanson\"}]}"
    let user = User(json: json)

ObjectMapper:

对象映射器

  • Another way is by using ObjectMapper. This gives more control but also takes a lot more code.
  • 另一种方法是使用 ObjectMapper。这提供了更多控制,但也需要更多代码。

Example:

例子:

    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 Hymanson\"}]}"
    let user = Mapper<User>().map(json)

回答by Peheje

I worked a bit on a smaller solution that doesn't require inheritance. But it hasn't been tested much. It's pretty ugly atm.

我在一个不需要继承的较小解决方案上做了一些工作。但它没有经过太多测试。这是非常丑陋的atm。

https://github.com/peheje/JsonSerializerSwift

https://github.com/peheje/JsonSerializerSwift

You can pass it into a playground to test it. E.g. following class structure:

您可以将其传递到 Playground 中进行测试。例如以下类结构:

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

prints

印刷

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

回答by Downgoat

This is not a perfect/automatic solution but I believe this is the idiomatic and nativeway to do such. This way you don't need any libraries or such.

这不是一个完美/自动的解决方案,但我相信这是这样做的惯用和原生方式。这样你就不需要任何图书馆之类的了。

Create an protocol such as:

创建一个协议,例如:

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

Then implement it in your class such as:

然后在您的类中实现它,例如:

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

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

Now:

现在:

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

Note: if you want jsonas a string, it is very simply to convert to a string: String(data: json, encoding .utf8)

注意:如果你想要json一个字符串,转换成一个字符串非常简单:String(data: json, encoding .utf8)

回答by dheeru

Some of the above answers are completely fine, but I added an extension here, just to make it much more readable and usable.

上面的一些答案完全没问题,但我在这里添加了一个扩展,只是为了使它更具可读性和可用性。

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

//This will print like the following:

//这将打印如下:

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

回答by zrzka

Not sure if lib/framework exists, but if you would like to do it automatically and you would like to avoid manual labour :-) stick with MirrorType...

不确定 lib/framework 是否存在,但是如果您想自动执行它并且您想避免体力劳动:-) 坚持使用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
  }

}

Take it as a rough example, lacks proper conversion support, lacks recursion, ... It's just MirrorTypedemonstration ...

举个粗略的例子,缺少适当的转换支持,缺少递归,……只是MirrorType演示……

P.S. Here it's done in U, but you're going to enhance NSManagedObjectand then you'll be able to convert all NSManagedObjectsubclasses. No need to implement this in all subclasses/managed objects.

PS 这里是在 中完成的U,但是您将要增强NSManagedObject,然后您将能够转换所有NSManagedObject子类。无需在所有子类/托管对象中实现这一点。

回答by Andrew

2020 | SWIFT 5.1:

2020 | 斯威夫特 5.1:

(also works with SWIFT 4)

(也适用于 SWIFT 4)



Ready to work solution!

准备工作解决方案!

Usage:

用法:

var msgTemplates = [msgTemlate]()

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

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


The following code resolve 3 things:

以下代码解决了 3 件事:

  • 1 string to save file
  • 1 string to load file
  • ability to get/print JSON of some Codable element with element.toJsonString
  • 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: and ofc data must be Codable:

PS:和ofc数据必须是可编码的:

struct msgTemlate: Codable { 
     //some params
}


PS2: in case of msgTemlate have enums, they are also must be Codable

PS2:如果 msgTemlate 有枚举,它们也必须是 Codable

回答by Pawan kumar sharma

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

Now just make your object like this

现在让你的对象像这样

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

让用户 = 用户(“1”,“pawan”)

do{
      let userJson = data: try JSONEncoder().encode(parentMessage), encoding:.utf8)

    }catch{
         fatalError("Unable To Convert in Json")      
    }

Then reconvert from json to Object

然后从json重新转换为Object

let jsonDecoder = JSONDecoder()
do{
   let convertedUser = try jsonDecoder.decode(User.self, from: userJson.data(using: .utf8)!)
 }catch{

 }