哪些网站可以做微信公司seo
在网络请求或接口签名中,通常要求将参数按照一定规则拼接成字符串。一个常见的做法是对字典的 key 进行排序,然后按照 “key=value” 的格式拼接,多个参数之间以特定符号(例如 &
)连接。
如果参数中包含嵌套的字典或数组,则需要递归展开。这样的处理不仅可以保证字符串的唯一性,同时也方便后续的加密或签名操作。
- 入参
- 一个可以转成json的字典或者数组
- 比如:["b": 2, "a": 1]
- 返回值
- 按照key进行排序后的字符串
-
比如:a=1&b=2
下面的算法不仅支持简单的键值对,还能够递归处理嵌套结构,使得所有数据都能被有序地转换为字符串形式,从而满足不同业务场景下的参数签名需求。
为什么非要自己实现呢?
- 这是因为字典是无序的,一个key-value完全相等的字典,转成jsonString是不一样的。
- 平台差异,iOS平台转成的json字符串和Java平台、go平台生成的字符串规则是不一样的,需要一个规则把无序的对象处理成有序的字符串
同一个平台,对同一个字典,生成json字符串结果都不一样。比如:
func testOrign() {let dictionary: [String: Any] = ["b": [1,["d": 4, "c": 3]],"a": ["e": 5,"f": [6, 7],"uid": 8,"age": 20.124,"student": true,"teacher": false,"dic":["rID":"123","author":"gcs",]],"image": ["10", "11", "12", "15"],"array":[[1,2,3],["1","2","3"],["rID":"123","author":"gcs"],["rID":"1234","author":"g"],]]// 把这个字典转出josn字符串,然后再转回字典,看看是否和原来的字典一样let jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: [])let jsonString = String(data: jsonData, encoding: .utf8)!print("jsonString",jsonString)do {let dictionary2 = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]let jsonData2 = try! JSONSerialization.data(withJSONObject: dictionary2, options: [])let jsonString2 = String(data: jsonData2, encoding: .utf8)!print("jsonString2",jsonString2)}}
实现思路
SignTool
类的核心在于两个方法:一个是用于处理字典的 concatenate(dictionary:)
,另一个是私有方法 concatenate(array:)
,用于处理数组。整个实现思路可以分为以下几个步骤:
-
字典的排序与遍历
- 首先,对传入的字典的所有 key 进行排序,确保每次拼接后的结果一致。
- 遍历排序后的 key,根据对应的 value 类型分别处理:
- 如果 value 是字典,则递归调用自身,将字典内容转换为字符串;
- 如果 value 是数组,则调用数组处理方法;
- 如果 value 是基本类型(例如数字或字符串),直接转换为字符串;
- 其他类型则按默认方式拼接,最后兜底。
-
数组的递归拼接
- 数组中的每个元素可能又是字典或数组,需要递归调用对应的方法。
- 每个元素转换后,通过逗号分隔,并在结束时移除多余的逗号。
-
格式规范
- 字典的 key-value 对之间使用
&
符号分隔; - 数组的元素间使用
,
分隔,并在嵌套拼接时加上{}
或[]
表示不同的数据结构。
- 字典的 key-value 对之间使用
代码实现
下面是完整的代码实现,并附有详细注释:
import Foundationclass SignTool {// 类方法,接收一个字典作为参数,返回拼接好的字符串class func concatenate(dictionary: [String: Any]) -> String {// 对字典的key进行排序let sortedKeys = dictionary.keys.sorted()var result = ""// 遍历排序后的keyfor (index, key) in sortedKeys.enumerated() {if let value = dictionary[key] {// 如果value是字典类型,递归调用concatenate方法if let valueDict = value as? [String: Any] {result += "\(key)={\(self.concatenate(dictionary: valueDict))}"// 如果value是数组类型,递归调用concatenate方法} else if let valueArray = value as? [Any] {result += "\(key)=[\(self.concatenate(array: valueArray))]"// 如果value是NSNumber类型,直接拼接} else if let numberValue = value as? NSNumber {result += "\(key)=\(numberValue)"// 如果value是字符串类型,直接拼接} else if let stringValue = value as? String {result += "\(key)=\(stringValue)"// 其他类型的value,直接拼接} else {result += "\(key)=\(value)"}}// 在key-value对之间添加&符号if index < sortedKeys.count - 1 {result += "&"}}return result}// 私有类方法,接收一个数组作为参数,返回拼接好的字符串private class func concatenate(array: [Any]) -> String {var result = ""// 遍历数组中的每个元素for value in array {// 如果元素是字典类型,递归调用concatenate方法if let valueDict = value as? [String: Any] {result += "{\(self.concatenate(dictionary: valueDict))},"// 如果元素是数组类型,递归调用concatenate方法} else if let valueArray = value as? [Any] {result += "[\(self.concatenate(array: valueArray))],"// 如果元素是NSNumber类型,直接拼接} else if let numberValue = value as? NSNumber {result += "\(numberValue),"// 如果元素是字符串类型,直接拼接} else if let stringValue = value as? String {result += "\(stringValue),"// 其他类型的元素,直接拼接} else {result += "\(value),"}}// 移除最后的逗号if result.hasSuffix(",") {result.removeLast()}return result}
}
实际效果
通过上述实现,可以将复杂的字典与数组数据转换为统一的字符串格式。例如:
输出结果类似于:
a={age=20.124&e=5&f=[6,7]&student=1&teacher=0&uid=8}&b=[1,{c=3&d=4}]&image=[10,11,12,15]
其他测试用例及代码:
class SignToolTests: NSObject {public static func test() {print("start test")let test = SignToolTests()test.testConcatenateWithSimpleDictionary()test.testConcatenateWithNestedDictionary()test.testConcatenateWithArray()test.testConcatenateWithNestedArray()test.testConcatenateWithComplexStructure()test.testConcatenate()test.testMax()print("end test")}func testConcatenateWithSimpleDictionary() {let dictionary: [String: Any] = ["b": 2, "a": 1]let result = SignTool.concatenate(dictionary: dictionary)XCTAssertEqual(result, "a=1&b=2")}func testConcatenateWithNestedDictionary() {let dictionary: [String: Any] = ["b": ["d": 4, "c": 3], "a": 1]let result = SignTool.concatenate(dictionary: dictionary)XCTAssertEqual(result, "a=1&b={c=3&d=4}")}func testConcatenateWithArray() {let dictionary: [String: Any] = ["b": [1, 2, 3], "a": 1]let result = SignTool.concatenate(dictionary: dictionary)XCTAssertEqual(result, "a=1&b=[1,2,3]")}func testConcatenateWithNestedArray() {let dictionary: [String: Any] = ["b": [1,["d": 4,"c": 3]],"a": 1]let result = SignTool.concatenate(dictionary: dictionary)XCTAssertEqual(result, "a=1&b=[1,{c=3&d=4}]")}func testConcatenateWithComplexStructure() {let dictionary: [String: Any] = ["b": [1,["d": 4, "c": 3]],"a": ["e": 5,"f": [6, 7]]]let result = SignTool.concatenate(dictionary: dictionary)XCTAssertEqual(result, "a={e=5&f=[6,7]}&b=[1,{c=3&d=4}]")}func testConcatenate() {let dictionary: [String: Any] = ["b": [1,["d": 4, "c": 3]],"a": ["e": 5,"f": [6, 7],"uid": 8,"age": 20.124,"student": true,"teacher": false,],"image": ["10", "11", "12", "15"],]let result1 = SignTool.concatenate(dictionary: dictionary)// 把这个字典转出josn字符串,然后再转回字典,看看是否和原来的字典一样let jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: [])let jsonString = String(data: jsonData, encoding: .utf8)!let dict = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]let result2 = SignTool.concatenate(dictionary: dict)print("jsonString",jsonString)print("result1",result1)print("result2",result2)XCTAssertEqual(result1, result2)}func testMax() {let dictionary: [String: Any] = ["b": [1,["d": 4, "c": 3]],"a": ["e": 5,"f": [6, 7],"uid": 8,"age": 20.124,"student": true,"teacher": false,"dic":["roomID":"123","author":"gcs",]],"image": ["10", "11", "12", "15"],"array":[[1,2,3],["1","2","3"],["roomID":"123","author":"gcs"],["roomID":"1234","author":"g"],]]let result1 = SignTool.concatenate(dictionary: dictionary)// 把这个字典转出josn字符串,然后再转回字典,看看是否和原来的字典一样let jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: [])let jsonString = String(data: jsonData, encoding: .utf8)!let dict = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]let result2 = SignTool.concatenate(dictionary: dict)print("jsonString",jsonString)print("result1",result1)print("result2",result2)XCTAssertEqual(result1, result2)}@discardableResultfunc XCTAssertEqual(_ str1: String, _ str2: String) -> Bool {if str1 == str2 {print("✅")return true}print("❌ Expected: \(str1) but got: \(str2)")return false}}