首页 > 解决方案 > Swift:在保留其密钥和结构的同时匿名化 JSON 内容的最简单、现代的方法是什么?

问题描述

这是我经常遇到的一个问题,但还没有一个好的解决方案。

我想采用具有不同结构和键名的任意 JSON,并通过基于 Swift 的转换运行它:

我已经成功地使用 Codable 编写了一次性转换,但它需要提前定义整个结构,因此它不适用于任意 JSON。它还需要自定义实现encode(to encoder: Encoder)来保留空值,这进一步使事情变得笨拙。

另一种方法是使用字符串扫描来检测所需字段并重写其内容吗?使用字典进行某种反思?还有什么?

标签: jsonswiftcodable

解决方案


可能这种方式适合您,并进行一些修改。首先,我们需要保留我们想要散列的字段。我把它放在secretFields. 我们还需要类似 .h 的散列函数randomString。当然,我们需要递归动态解码HashCodable

import Foundation

let jsonData = """{"city":"New York","secret_field": {"first name": "Adam","last name":"Smith"}}"""

// [1]
let secretFIelds = ["first name", "last name"]

// [2]
func randomString(length: Int) -> String {
  let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  return String((0..<length).map{ _ in letters.randomElement()! })
}

// [3]
struct HashCodable: Decodable {
  var value: Any

  struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  init(value: Any) {
    self.value = value
  }

  init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        if secretFIelds.contains(key.stringValue) {
            result[key.stringValue] = randomString(length:10)
        } else {
            result[key.stringValue] = 
                try container.decode(HashCodable.self, forKey: key).value
        }    
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(HashCodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(
            in: container, debugDescription: 
              "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(
          DecodingError.Context(codingPath: decoder.codingPath, 
            debugDescription: "Could not serialise"))
    }
  }
}

let decoded = try! JSONDecoder().decode(HashCodable.self, from: jsonData.data(using: .utf8)!)
print(decoded)

输入 JSON:

 {
   "city":"New York",
   "secret_field": {
      "first name": "Adam",
      "last name": "Smith"  
    }
 }```

输出:

HashCodable(value: [
  "city": "New York", 
  "secret_fields": [
    "last name": "3R6ocxYS44",
    "first name": "uCFgajZQY7"
   ]
])```

推荐阅读