繁体   English   中英

Swift:将字典转换为 JSON 用于 multipart/Form-Data

[英]Swift: Convert Dictionary into JSON for multipart/Form-Data

我有一个 swift 应用程序,它向服务器发出具有正常值和图像的请求,因此我使用的是多部分/表单数据请求。 我有一个 object,它具有如下所示的所有值:

 struct parameters: Codable {
     var request : String = "blah"
     var arrayData : [String] = ["bleh", "bluh"]
     var dictionaryData : [String: String] = ["1": "Blah", "2": "Blah"]
 }

但是,我无法将 arrays 的 json 转换版本和字典插入 httpBody。 我试过使用 JSONEncoder(),但它会将整个 object 转换为 JSON,而且我无法插入边界和其他内容。 有什么方法可以将 arrays & 字典转换为 json 格式,然后我可以将其作为值插入到多部分/表单数据请求中。

例如,对于字符串、字典和数组,它将返回

 "blah"
 ["bleh", "bluh"]
 {"1": "Blah", "2": "Blah"}

let dictParam = NSMutableDictionary() //字典

var ids = [String]()
let cat = (Constants.app_delegate.dictAddPostInfo.object(forKey: "categoryArr") as! NSArray)

//filter id from dic and add in ids
for dic in cat {
    let dic = dic as! NSDictionary
    let id = dic["id"] as! Int
    ids.append(String(id))
}
//set data to dicParam
for i in 0..<ids.count {
    let key = String.init(format: "categories[%d]", i)
    dictParam.setValue(ids[i], forKey: key)
}


//For single value 
dictParam.setValue(Constants.app_delegate.dictAddPostInfo.object(forKey: "Title") as! String, forKey: "title")
dictParam.setValue(Constants.app_delegate.dictAddPostInfo.object(forKey: "Description") as! String, forKey: "description")
dictParam.setValue(Constants.app_delegate.dictAddPostInfo.object(forKey: "Price") as! String, forKey: "price")
dictParam.setValue(latitude, forKey: "latitude")
dictParam.setValue(longitude, forKey: "longitude")

if let area = Constants.app_delegate.dictAddPostInfo.object(forKey: "area") as? String {
    dictParam.setValue(area, forKey: "area")
}
else {
    dictParam.setValue("", forKey: "area")
}

if let city = Constants.app_delegate.dictAddPostInfo.object(forKey: "city") as? String {
    dictParam.setValue(city, forKey: "city")
}
else {
    dictParam.setValue("", forKey: "city")
}


if let state = Constants.app_delegate.dictAddPostInfo.object(forKey: "state") as? String {
    dictParam.setValue(state, forKey: "state")
}
else {
    dictParam.setValue("", forKey: "state")
}


if let zip = Constants.app_delegate.dictAddPostInfo.object(forKey: "zip") as? String {
    dictParam.setValue(zip, forKey: "zip")
}
else {
    dictParam.setValue("", forKey: "zip")
}

这里将字典转换为 json 的代码

   if let jsonData =  try? JSONSerialization.data(withJSONObject: dictParam, options: .prettyPrinted), let theJSONText = String(data: jsonData, encoding: .ascii) {
        print(theJSONText)
    }

您可以单独对各个组件进行编码,而不是对整个结构进行编码。 使用上面的例子:

let paramters = Parameters()
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let request = try! encoder.encode(paramters.request)
let array = try! encoder.encode(paramters.arrayData)
let dict = try! encoder.encode(paramters.dictionaryData)

这将为您单独提供组件,然后您可以访问它们并按照您的喜好使用它们。 作为一个简单的例子:

print("Request \n", String(data: request, encoding: .utf8)!, "\n\n")
print("Array \n", String(data: array,encoding: .utf8)!, "\n\n")
print("Dict \n", String(data: dict ,encoding: .utf8)!, "\n\n")

正在输出...

要求

“废话”

大批

[“嘘”,“蓝”]

字典

{“2”:“废话”,“1”:“废话”}

这是一个多部分编码器,您可以直接将其转换为多部分表单数据。 我根据这个答案制作的, Custom Swift Encoder/Decoder for the Strings Resource Format

import Foundation

/// An object that encodes instances of a data type
/// as strings following the simple strings file format.
public class MultipartEncoder {
    
    var boundary: String = ""
    /// Returns a strings file-encoded representation of the specified value.
    public func encode<T: Encodable>(_ value: T) throws -> Data {
        let multipartencoding = MultipartEncoding()
        try value.encode(to: multipartencoding)
        return dataFromFormat(from: multipartencoding.data.strings)
    }
    
    private func dataFromFormat(from strings: [String: String]) -> Data {
        let lineBreak = "\r\n"
        //return dotStrings.joined(separator: "\n")
        var fieldData = Data()
        for (key, value) in strings{
            fieldData.append("--\(boundary + lineBreak)")
            fieldData.append("Content-Disposition: form-data; name=\"\(key)\"\(lineBreak + lineBreak)")
            fieldData.append(value)
            fieldData.append(lineBreak)
        }
        
        
        print("multipartdata \(String(data: fieldData, encoding: .ascii) )")
        
        return fieldData as Data
    }
}


fileprivate struct MultipartEncoding: Encoder {
    
    /// Stores the actual strings file data during encoding.
    fileprivate final class dictData {
        private(set) var strings: [String: String] = [:]
        
        func encode(key codingKey: [CodingKey], value: String) {
            let key = codingKey.map { $0.stringValue }.joined(separator: ".")
            strings[key] = value
        }
    }
    
    fileprivate var data: dictData
    
    init(to encodedData: dictData = dictData()) {
        self.data = encodedData
    }

    var codingPath: [CodingKey] = []
    
    let userInfo: [CodingUserInfoKey : Any] = [:]
    
    func container<Key: CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
        var container = StringsKeyedEncoding<Key>(to: data)
        print("in container keyed")
        container.codingPath = codingPath
        return KeyedEncodingContainer(container)
    }
    
    func unkeyedContainer() -> UnkeyedEncodingContainer {
        var container = StringsUnkeyedEncoding(to: data)
        container.codingPath = codingPath
        return container
   }
    
    func singleValueContainer() -> SingleValueEncodingContainer {
        var container = StringsSingleValueEncoding(to: data)
        container.codingPath = codingPath
        return container
    }
}

fileprivate struct StringsKeyedEncoding<Key: CodingKey>: KeyedEncodingContainerProtocol {

    private let data: MultipartEncoding.dictData
    
    init(to data: MultipartEncoding.dictData) {
        self.data = data
    }
    
    var codingPath: [CodingKey] = []
    
    mutating func encodeNil(forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: "nil")
    }
    
    mutating func encode(_ value: Bool, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: String, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value)
    }
    mutating func encode(_ value: Date, forKey key: Key) throws {
        var formatter = getDayFormatter()
        print("value is \(formatter.string(from: value))")
        data.encode(key: codingPath + [key], value:  formatter.string(from: value))
    }
    mutating func encode(_ value: Double, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Float, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Int, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Int8, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Int16, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Int32, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: Int64, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: UInt, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: UInt8, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: UInt16, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: UInt32, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode(_ value: UInt64, forKey key: Key) throws {
        data.encode(key: codingPath + [key], value: value.description)
    }
    
    mutating func encode<T: Encodable>(_ value: T, forKey key: Key) throws {
        if T.self == Date.self{
            var formatter = getDayFormatter()
            print("value is \(formatter.string(from: value as! Date))")
            data.encode(key: codingPath + [key], value:  formatter.string(from: value as! Date))
        }else{
            var stringsEncoding = MultipartEncoding(to: data)
            stringsEncoding.codingPath.append(key)
            try value.encode(to: stringsEncoding)
        }
    }
    
    mutating func nestedContainer<NestedKey: CodingKey>(
        keyedBy keyType: NestedKey.Type,
        forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
        var container = StringsKeyedEncoding<NestedKey>(to: data)
        container.codingPath = codingPath + [key]
        return KeyedEncodingContainer(container)
    }
    
    mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
        var container = StringsUnkeyedEncoding(to: data)
        container.codingPath = codingPath + [key]
        return container
    }
    
    mutating func superEncoder() -> Encoder {
        let superKey = Key(stringValue: "super")!
        return superEncoder(forKey: superKey)
    }
    
    mutating func superEncoder(forKey key: Key) -> Encoder {
        var stringsEncoding = MultipartEncoding(to: data)
        stringsEncoding.codingPath = codingPath + [key]
        return stringsEncoding
    }
}

fileprivate struct StringsUnkeyedEncoding: UnkeyedEncodingContainer {

    private let data: MultipartEncoding.dictData
    
    init(to data: MultipartEncoding.dictData) {
        self.data = data
    }
    
    var codingPath: [CodingKey] = []

    private(set) var count: Int = 0
    
    private mutating func nextIndexedKey() -> CodingKey {
        let nextCodingKey = IndexedCodingKey(intValue: count)!
        count += 1
        return nextCodingKey
    }
    
    private struct IndexedCodingKey: CodingKey {
        let intValue: Int?
        let stringValue: String

        init?(intValue: Int) {
            self.intValue = intValue
            self.stringValue = intValue.description
        }

        init?(stringValue: String) {
            return nil
        }
    }

    mutating func encodeNil() throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: "nil")
    }
    
    mutating func encode(_ value: Bool) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    mutating func encode(_ value: Date) throws {
        let formatter = getDayFormatter()
        print("value2 is \(formatter.string(from: value))")
        data.encode(key: codingPath + [nextIndexedKey()], value: formatter.string(from: value))
    }
    mutating func encode(_ value: String) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value)
    }
    
    mutating func encode(_ value: Double) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Float) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Int) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Int8) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Int16) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Int32) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: Int64) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: UInt) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: UInt8) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: UInt16) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: UInt32) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode(_ value: UInt64) throws {
        data.encode(key: codingPath + [nextIndexedKey()], value: value.description)
    }
    
    mutating func encode<T: Encodable>(_ value: T) throws {
        var stringsEncoding = MultipartEncoding(to: data)
        stringsEncoding.codingPath = codingPath + [nextIndexedKey()]
        try value.encode(to: stringsEncoding)
    }
    
    mutating func nestedContainer<NestedKey: CodingKey>(
        keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
        var container = StringsKeyedEncoding<NestedKey>(to: data)
        container.codingPath = codingPath + [nextIndexedKey()]
        return KeyedEncodingContainer(container)
    }
    
    mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
        var container = StringsUnkeyedEncoding(to: data)
        container.codingPath = codingPath + [nextIndexedKey()]
        return container
    }
    
    mutating func superEncoder() -> Encoder {
        var stringsEncoding = MultipartEncoding(to: data)
        stringsEncoding.codingPath.append(nextIndexedKey())
        return stringsEncoding
    }
}
fileprivate struct StringsSingleValueEncoding: SingleValueEncodingContainer {
    
    private let data: MultipartEncoding.dictData
    
    init(to data: MultipartEncoding.dictData) {
        self.data = data
    }

    var codingPath: [CodingKey] = []
    
    mutating func encodeNil() throws {
        data.encode(key: codingPath, value: "nil")
    }
    
    mutating func encode(_ value: Bool) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: String) throws {
        data.encode(key: codingPath, value: value)
    }
    
    mutating func encode(_ value: Double) throws {
        data.encode(key: codingPath, value: value.description)
    }
    mutating func encode(_ value: Date) throws {
        let formatter = getDayFormatter()
        print("value3 is \(formatter.string(from: value))")
        data.encode(key: codingPath, value: formatter.string(from: value))
    }
    mutating func encode(_ value: Float) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: Int) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: Int8) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: Int16) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: Int32) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: Int64) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: UInt) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: UInt8) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: UInt16) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: UInt32) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode(_ value: UInt64) throws {
        data.encode(key: codingPath, value: value.description)
    }
    
    mutating func encode<T: Encodable>(_ value: T) throws {
        var stringsEncoding = MultipartEncoding(to: data)
        stringsEncoding.codingPath = codingPath
        try value.encode(to: stringsEncoding)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM