#arrays #json #swift #dictionary
#массивы #json #swift #словарь
Вопрос:
Мне нужно синхронизировать два json-файла, чтобы добавить новое содержимое из файла A (расположенного в пакете приложений) в файл B после обновления приложения.
Оба json-файла являются массивами словарей. Мне нужно повторить словари из файла A, и на основе значения «id», если словарь отсутствует в файле B, мне нужно добавить эти отсутствующие словари и сохранить файл B обратно в файловую систему.
У меня есть решение ниже, которое делает это, и, кажется, работает. Но это ТАК некрасиво! Конечно, я собрал это вместе примерно за 15 минут, весь съежившись, но я уверен, что должен быть лучший способ справиться с этим. Кроме того, я не хочу еще больше мутить воду, преобразуя эти словари в структуры или модели только для сравнения, чтобы преобразовать их обратно в dictionary -> json.
Любой совет здесь был бы отличным! Я предпочитаю чистый код, а это беспорядок.
typealias JSON = [[String: Any]]
static private func uglySync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" fileName ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
do {
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
do {
let destinationData = try Data(contentsOf: destinationUrl)
do {
if let destinationArray = try JSONSerialization.jsonObject(with: destinationData, options: .mutableContainers) as? JSON {
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch let error as NSError {
print("Couldn't write to file: (error.localizedDescription)")
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
// oh wow the try catches :/
}
Ответ №1:
Я сгруппировал преобразование файлов в JSONArray, чтобы упростить выполнение … catch. В качестве альтернативы, если вам не нужно печатать сообщение об ошибке, вы также можете выбрать необязательный try?
, чтобы удалить блок do … catch.
typealias JSONArray = [[String: Any]]
private func jsonArray(from fileURL: URL) -> JSONArray? {
do {
let fileData: Data = try Data(contentsOf: fileURL)
guard let jsonArray = (try JSONSerialization.jsonObject(with: fileData, options: .mutableContainers)) as? JSONArray else {
debugPrint("Failed to find JSON Array table")
return nil
}
return jsonArray
} catch {
print(error.localizedDescription)
return nil
}
}
func sync() {
let fileName: String = "someFileName"
guard
let fileURL: URL = Bundle.main.url(forResource: fileName, withExtension: "json"),
let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let destinationURL: URL = destinationDirectory.appendingPathComponent("Data/" fileName ".json"),
let sourceArray = jsonArray(from: fileURL),
let destinationArray = jsonArray(from: destinationURL)
else {
return
}
var mutableArray = destinationArray
let destinationIDArray = destinationArray.compactMap { $0["id"] as? String }
mutableArray.forEach { (item) in
if let itemId = item["id"] as? String, !(destinationIDArray.contains { $0 == itemId }) {
mutableArray.append(item)
}
}
// Update File
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationURL)
} catch {
print("Couldn't write to file: (error.localizedDescription)")
}
}
Комментарии:
1. Мне действительно нравится это решение! Я добавил это в свой проект, и после нескольких небольших настроек это отлично работает. Чистый и удобный для чтения. Спасибо!
Ответ №2:
Я думаю, вы можете поместить разные try
s в один do
блок.
do {
try function1()
try function2()
} catch {
print(error.localizedDescription)
}
Таким образом, впоследствии ваша функция может выглядеть следующим образом
typealias JSON = [[String: Any]]
static private func moderatelyOkSync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" fileName ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
let destinationData = try Data(contentsOf: destinationUrl)
}
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch {
print(error.localizedDescription)
}
}
Комментарии:
1. Совершенно верно. Я был немного многословен. Что вы думаете об аспектах загрузки / сравнения / сохранения? Спасибо за быстрый ответ!
2. @mejim707 Сохранение и загрузка тоже выглядят нормально, возможно, извлечь их в отдельную функцию. Сравнение тоже не слишком убого.
Ответ №3:
Способ, которым я бы это сделал, — это декодировать json-файлы с помощью struct, а затем закодировать (сериализовать) их в другие файлы. Потому что код для этого будет состоять из двух строк, но сначала вам придется расположить все переменные в структуре. Вероятно, все еще не оптимально