在Swift中编写带有排序键的prettyPrinted JSON对象

时间:2017-09-04 07:43:29

标签: json swift nsjsonserialization

我们经常希望将JSON用于人类可读性。因此,通常要求按字母顺序(或字母数字)in Goin .NETin Pythonin Java,...

对JSON键进行排序

但是如何在Swift中按字母顺序输出带有JSON键的JSON?

PrettyPrinted输出很简单:

JSONSerialization.writeJSONObject(jsonObject, to: outputStream, options: [.prettyPrinted], error: nil)

然而,按键不按字母顺序排列以供人类阅读。它们可能按NSDictionary.keyEnumerator()给出的顺序排列。但遗憾的是,我们不能在Swift中对Dictionary,NSDictionary或CFDictionary进行子类化,因此我们无法覆盖键顺序的行为。

[编辑:实际上,我们可以继承NSDictionary,请参阅下面的一个答案]

4 个答案:

答案 0 :(得分:8)

对于iOS 11+和macOS High Sierra(10.13 +),新选项.sortedKeys可以轻松解决问题:

JSONSerialization.writeJSONObject(jsonObject, to: outputStream, options: [.sortedKeys, .prettyPrinted], error: nil)

感谢Hamish提示。

答案 1 :(得分:1)

适用于iOS 7 +,macOS 10.9+(OS X Mavericks及以上版本)的Pure Swift解决方案。

解决方法是subclass NSDictionary但不是INIT方法(因为它不能用Swift 3或4编译)。

class MutableOrderedDictionary: NSDictionary {
    let _values: NSMutableArray = []
    let _keys: NSMutableOrderedSet = []

    override var count: Int {
        return _keys.count
    }
    override func keyEnumerator() -> NSEnumerator {
        return _keys.objectEnumerator()
    }
    override func object(forKey aKey: Any) -> Any? {
        let index = _keys.index(of: aKey)
        if index != NSNotFound {
            return _values[index]
        }
        return nil
    }
    func setObject(_ anObject: Any, forKey aKey: String) {
        let index = _keys.index(of: aKey)
        if index != NSNotFound {
            _values[index] = anObject
        } else {
            _keys.add(aKey)
            _values.add(anObject)
        }
    }
}

然后我们可以订购对象的键:

let orderedJson = MutableOrderedDictionary()
jsonObject.sorted { $0.0.compare($1.0, options: [.caseInsensitive]) == .orderedAscending }
          .forEach { orderedJson.setObject($0.value, forKey: $0.key) }

最后我们可以把它写成漂亮印刷:

_ = JSONSerialization.writeJSONObject(orderedJson, to: outputJSON, options: [.prettyPrinted], error: nil)

但要小心:您需要对JSON对象的所有子类进行子类化和排序。

答案 2 :(得分:0)

以下是一种可行的解决方法,仅适用于macOS Swift脚本不适用于iOS

我们使用不同的编程语言解决Swift Foundation的限制(例如 Python 2.7 )。

import Cocoa

// sample jsonObject
let jsonObject = ["hello": "world", "foo": "bar"]

// writing JSON to file
let jsonPath = "myJson.json"
let outputJSON = OutputStream(toFileAtPath: jsonPath, append: false)!
outputJSON.open()
_ = JSONSerialization.writeJSONObject(jsonObject, to: outputJSON, options: [], error: nil)
outputJSON.close()

// sortedKeys equivalent using `sort_keys=True`
// prettyPrinted equivalent using `indent=2, separators=(',', ' : ')`
// unicode using `io.open` and `ensure_ascii=False`
func shell(_ args: String...) {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    task.launch()
    task.waitUntilExit()
}
shell("python", "-c", ""
    + "import io\n"
    + "import json\n"
    + "jsonPath = 'myJson.json'\n"
    + "with io.open(jsonPath, mode='r', encoding='utf8') as json_file:\n"
    + "    all_data = json.load(json_file)\n"
    + "with io.open(jsonPath, mode='w', encoding='utf8') as json_file:\n"
    + "    json_file.write(unicode(json.dumps(all_data, ensure_ascii=False, sort_keys=True, indent=2, separators=(',', ' : '))))\n"
)

答案 3 :(得分:0)

适用于iOS 7 +,macOS 10.9+(OS X Mavericks及以上版本)的解决方案。

解决方案是在 Objective-C 中继承NSDictionary,然后使用框架(对于Application)或静态库(对于命令行工具)的子类。

对于此演示,我将使用nicklockwood/OrderedDictionary(700多行代码)而不是从头开始,但可能有未经测试的替代方案,如quinntaylor/CHOrderedDictionaryrhodgkins/RDHOrderedDictionary。要将其集成为Framework,请在PodFile中添加此依赖项:

pod 'OrderedDictionary', '~> 1.4'

然后我们将订购对象的键:

import OrderedDictionary

let orderedJson = MutableOrderedDictionary()
jsonObject.sorted { $0.0.compare($1.0, options: [.caseInsensitive]) == .orderedAscending }
          .forEach { orderedJson.setObject($0.value, forKey: $0.key) }

(注意:setObject(_,forKey:)特定于MutableOrderedDictionary)
最后我们可以把它写得漂亮印刷:

_ = JSONSerialization.writeJSONObject(orderedJson, to: outputJSON, options: [.prettyPrinted], error: nil)

但要小心:您需要对JSON对象的所有子类进行子类化和排序。