简体   繁体   English

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

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

I have a swift app, that makes requests to the servers with both normal values and Images, so I am using a multipart/form-data request.我有一个 swift 应用程序,它向服务器发出具有正常值和图像的请求,因此我使用的是多部分/表单数据请求。 I have an object that has all my values like below:我有一个 object,它具有如下所示的所有值:

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

However, I am having trouble inserting the json converted version of the arrays and dictionaries into the httpBody.但是,我无法将 arrays 的 json 转换版本和字典插入 httpBody。 I've tried using JSONEncoder(), but that converts the entire object into JSON, and I can't insert boundaries and other things.我试过使用 JSONEncoder(),但它会将整个 object 转换为 JSON,而且我无法插入边界和其他内容。 Is there any way to convert just the arrays & dictionaries into json format which I can then insert as the values in a multipart/form-data request.有什么方法可以将 arrays & 字典转换为 json 格式,然后我可以将其作为值插入到多部分/表单数据请求中。

For example, for the string, dictionary, and array it would return例如,对于字符串、字典和数组,它将返回

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

let dictParam = NSMutableDictionary() //Dictionary 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")
}

Here the code of convert dictionary into json这里将字典转换为 json 的代码

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

You can just encode the individual components separately, rather than the struct as a whole.您可以单独对各个组件进行编码,而不是对整个结构进行编码。 Using the above example:使用上面的例子:

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)

which will give you the components individually, and you can then access them and use them however you fancy.这将为您单独提供组件,然后您可以访问它们并按照您的喜好使用它们。 As a trivial example:作为一个简单的例子:

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

Outputting...正在输出...

Request要求

"blah" “废话”

Array大批

[ "bleh", "bluh" ] [“嘘”,“蓝”]

Dict字典

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

here is a multipart encoder which you can directly convert into multipart form-data.这是一个多部分编码器,您可以直接将其转换为多部分表单数据。 i made from this answer, Custom Swift Encoder/Decoder for the Strings Resource Format我根据这个答案制作的, 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