簡體   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