0

我有Data一个包含来自服务器的 JSON 响应的对象。

它以Codable这种方式转换为某个对象:

let object = try JSONDecoder().decode(Object.self, from: response.data)

出于测试目的,我想将此对象结束编码Data,然后与 initial 进行比较Data

let data = try JSONEncoder().encode(object)

第一个假设:如果Data解码+编码后对象相等,则意味着所有字段都正确列在 my 中Codable struct,所有字段都具有有效类型等...转换后我有两个Data对象:154362 字节 154435 字节。这意味着它们是不同的。但是当我使用jsondiff.com比较它们时,它们似乎是 100% 相同的。

第二个假设:我试图将数据对象转换为字符串,但 JSON 结构以不同的方式排序......所以这种方式不起作用。

Double/Float值存在一些问题。它们在解码过程中以这种方式解释:41.01结果为41.009999999

所以问题是:有没有办法验证两个 JSON 对象在解码 + 编码前后是否相同?

当前解决方案:我决定JSONSerialization尝试一下,因为从 iOS 11 开始,它有一个很好的写作选择:

@available(iOS 11.0, *)
public static var sortedKeys: JSONSerialization.WritingOptions { get }

这样我转换Data为正确排序的json:

@available(iOS 11, *)
private extension Data {

    static func equal(json1: Data, json2: Data) -> Bool {
        return json1.serialized == json2.serialized
    }

    var serialized: Data? {
        guard let json = try? JSONSerialization.jsonObject(with: self) else {
            return nil
        }
        guard let data = try? JSONSerialization.data(withJSONObject: json, options: [.sortedKeys, .prettyPrinted]) else {
            return nil
        }
        return data
    }
}

这是比较两个jsonData对象的可靠方法吗?

4

1 回答 1

3

我创建了一个小的iOS11 Data扩展,它可以帮助我比较 JSON 对象并找到任何两个 JSON 对象开始不匹配的第一行。

@available(iOS 11, *)
public extension Data {

    public func jsonSerialized() -> Data? {
        guard let json = try? JSONSerialization.jsonObject(with: self) else {
            return nil
        }
        let object: Any = {
            if let array = json as? Array<Any> {
                return array.strippingNulls()
            } else if let dictionary = json as? Dictionary<String, Any> {
                return dictionary.strippingNulls()
            } else {
                return json
            }
        }()
        guard let data = try? JSONSerialization.data(withJSONObject: object, options: [.sortedKeys, .prettyPrinted]) else {
            return nil
        }
        return data
    }

    public static func jsonMismatch(lhs: Data, rhs: Data, alreadySerialized: Bool = false) -> Int? {
        switch alreadySerialized {
        case true:
            return _jsonMismatch(lhs: lhs, rhs: rhs)
        case false:
            guard let lhs = lhs.jsonSerialized(), let rhs = rhs.jsonSerialized() else {
                return nil
            }
            return _jsonMismatch(lhs: lhs, rhs: rhs)
        }
    }

    private static func _jsonMismatch(lhs: Data, rhs: Data) -> Int? {
        guard let string1 = String(data: lhs, encoding: .utf8), let string2 = String(data: rhs, encoding: .utf8) else {
            return nil
        }
        let components1 = string1.components(separatedBy: "\n")
        let components2 = string2.components(separatedBy: "\n")
        let count = components1.count < components2.count ? components1.count : components2.count
        for index in 0 ..< count {
            if components1[index] != components2[index] {
                return index
            }
        }
        return nil
    }
}

private extension Array where Element == Any {

    func strippingNulls() -> Array {
        var array = self
        array.stripNulls()
        return array
    }

    mutating func stripNulls() {
        let count = self.count
        guard count > 0 else {
            return
        }
        for _index in 0 ..< count {
            let index = count - 1 - _index
            if self[index] is NSNull {
                remove(at: index)
            } else if let array = self[index] as? [Any] {
                self[index] = array.strippingNulls()
            } else if let dictionary = self[index] as? [String: Any] {
                self[index] = dictionary.strippingNulls()
            }
        }
    }
}

private extension Dictionary where Key == String, Value == Any {

    func strippingNulls() -> Dictionary {
        var dictionary = self
        dictionary.stripNulls()
        return dictionary
    }

    mutating func stripNulls() {
        for (key, value) in self {
            if value is NSNull {
                removeValue(forKey: key)
            } else if let array = value as? [Any] {
                self[key] = array.strippingNulls()
            } else if let dictionary = value as? [String: Any] {
                self[key] = dictionary.strippingNulls()
            }
        }
    }
}
于 2017-11-03T16:20:48.147 回答