【问题标题】:How to serialize or convert Swift objects to JSON?如何将 Swift 对象序列化或转换为 JSON?
【发布时间】:2015-04-13 06:25:21
【问题描述】:

下面这个类

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

需要转换成

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

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

【问题讨论】:

  • 最好的办法是遍历它并将其保存到数组和字典中,然后转换。
  • @Schemetrical 你能给我举个例子吗?我不知道如何遍历一个对象。
  • 嗯,你需要遍历对象本身,获取所有值,然后手动将其添加到字典中,然后重复。
  • @Schemetrical 我确实试过了。但是对于大型对象,编译时间会急剧增加。

标签: json swift


【解决方案1】:

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

【讨论】:

  • 编码类型应该是.utf8而不是.utf16
  • @ChanJingHong 这取决于您要编码的内容
  • 对于这个具体的例子,.utf16 有效吗?我试过了,还是不行。
  • @ChanJingHong 奇怪,我在 3 个月前尝试过,它成功了。我认为我应该将编码类型也作为解码方法中的参数。我去看看。
  • 问题是关于编码class(我需要的)而不是struct
【解决方案2】:

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

Swift 对象转 JSONString

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

你可以在这里找到所有的细节和例子Ultimate Guide to JSON Parsing With Swift 4

【讨论】:

    【解决方案3】:

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

    EVReflection

    • 这是反射原理的作品。这需要更少的代码并且还支持NSDictionaryNSCodingPrintableHashableEquatable

    示例:

        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 抱歉,我不确定这是否可能。
    • @Suresh:可以通过编写自定义转换,如所示示例。我将图像转换为字符串,然后添加到 Json 对象中。特别是与手表操作系统一起工作时,它有很大帮助
    • 您好,您知道如何初始化一个 Mappable 类并手动设置属性,然后将对象转换为 jsonString 吗?
    • swift 4/ios 11 的可编码协议有什么影响?? .我们可以使用这个在 swift 4 中将 NSManagedObject 转换为 JSON 吗??
    【解决方案4】:

    我在一个不需要继承的较小解决方案上做了一些工作。但它没有经过太多测试。自动取款机太丑了。

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

    【讨论】:

    • 你能提供它的swift 2.3版本吗?
    【解决方案5】:

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

    创建一个协议,例如:

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

      【讨论】:

        【解决方案7】:

        不确定 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
          }
        
        }
        

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

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

        【讨论】:

        • 嗨,我正在尝试这种方法,但每次我得到 0 作为计数。您能否更具体地说明我应该做什么?我认为@NSManaged 引起了问题。如何增强 NSManagedObject ?
        • 没有尝试使用@NSManaged。也许它导致了你的问题。在这种情况下,我会用 Objective-C 编写它,然后在 Swift 中使用它。
        【解决方案8】:

        2021 |斯威夫特 5.1 |结果解决方案

        输入数据:

        struct ConfigCreds: Codable {
            // some params
        }
        

        用法:

        // get JSON from Object
        configCreds
           .asJson()
           .onSuccess{ varToSaveJson = $0 }
           .onFailure{ _ in // any failure code }
        
        // get object of type "ConfigCreds" from JSON
        someJsonString
            .decodeFromJson(type: ConfigCreds.self)
            .onSuccess { configCreds = $0 }
            .onFailure{ _ in // any failure code }
        

        返回码:

        @available(macOS 10.15, *)
        public extension Encodable {
            func asJson() -> Result<String, Error>{
                JSONEncoder()
                    .try(self)
                    .flatMap{ $0.asString() }
            }
        }
        
        public extension String {
            func decodeFromJson<T>(type: T.Type) -> Result<T, Error> where T: Decodable {
                self.asData()
                    .flatMap { JSONDecoder().try(type, from: $0) }
            }
        }
        
        ///////////////////////////////
        /// HELPERS
        //////////////////////////////
        
        @available(macOS 10.15, *)
        fileprivate extension JSONEncoder {
            func `try`<T : Encodable>(_ value: T) -> Result<Output, Error> {
                do {
                    return .success(try self.encode(value))
                } catch {
                    return .failure(error)
                }
            }
        }
        
        fileprivate extension JSONDecoder {
            func `try`<T: Decodable>(_ t: T.Type, from data: Data) -> Result<T,Error> {
                do {
                    return .success(try self.decode(t, from: data))
                } catch {
                    return .failure(error)
                }
            }
        }
        
        fileprivate extension String {
            func asData() -> Result<Data, Error> {
                if let data = self.data(using: .utf8) {
                    return .success(data)
                } else {
                    return .failure(WTF("can't convert string to data: \(self)"))
                }
            }
        }
        
        fileprivate extension Data {
            func asString() -> Result<String, Error> {
                if let str = String(data: self, encoding: .utf8) {
                    return .success(str)
                } else {
                    return .failure(WTF("can't convert Data to string"))
                }
            }
        }
        
        fileprivate func WTF(_ msg: String, code: Int = 0) -> Error {
            NSError(code: code, message: msg)
        }
        

        【讨论】:

        • 不确定如何使用它。 “'JSONEncoder.Output' 类型的值没有成员 'asString'”和“'String' 类型的值没有成员 'asData'”的 IDE 错误
        【解决方案9】:
        struct User:Codable{
         var id:String?
         var name:String?
         init(_ id:String,_ name:String){
           self.id  = id
           self.name = name
         }
        }
        

        现在让你的对象像这样

        让 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{
           
         }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-03
          • 1970-01-01
          • 2018-02-16
          • 2015-07-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多