Вложенный JSON во вложенный словарь с быстрым

Я использую новый протокол Codable, чтобы преобразовать структуру в JSON, а затем в словарь для целей тестирования. Проблема в том, что переменная словаря внутри структуры не преобразуется обратно и остается Any, а не [Int: String].

struct Person: Codable {
    var name: String?
    var history: [Int: String]

    init() {
        self.name = "Name"
        history = [0: "Test"]
    }
}

let person = Person()

let jsonData = try JSONEncoder().encode(person)

let result = try JSONSerialization.jsonObject(with: jsonData, options: [])

let dictionary = result as? [String: Any]

print(dictionary)

Это дает мне следующий результат

Optional(["history": {
    0 = Test;
}, "name": Name])

Когда я ожидал

Optional(["history":[0: "Test"]], "name": "Test"])

Я был бы признателен за любое объяснение того, почему это происходит, или, что еще лучше, за решение, как в основном выполнять глубокую сериализацию JSON.

Я добавляю игровую площадку, демонстрирующую проблему: https://www.dropbox.com/s/igpntk7az0hevze/JSONSerialisation.playground.zip


person Denis Balko    schedule 19.07.2017    source источник
comment
Почему вы ожидаете, что это будет выглядеть именно так? Словарь, который вы печатаете, является полноценным словарем, и данные выглядят именно так, как и должно быть. Имейте в виду, что [Int: String] - это не массив, это словарь, а {0 = Test;} - это то, как Swift форматирует словарь, когда вы печатаете его на консоли для отладки... Мне кажется, что ваш код работает нормально.   -  person donnywals    schedule 19.07.2017
comment
Привет, donnywals, на самом деле Swift печатает словарь не так. Если я определяю словарь как let sampleDictionary: [String: Any] = ["history":[0: "Test"], "name": "Test"], а затем печатаю его print(sampleDictionary), результатом будет ["history": [0: "Test"], "name": "Test"]. И если я попытаюсь получить доступ к истории с помощью let history = dictionary["history"] as? [Int: String], значение будет равно нулю, потому что это не словарь.   -  person Denis Balko    schedule 19.07.2017
comment
Упс.. Вы, кажется, абсолютно прав Денис!   -  person donnywals    schedule 20.07.2017


Ответы (2)


Поскольку вы использовали JSONEncoder для кодирования, вы также можете использовать JSONDecoder для декодирования данных json. В этом случае history — это словарь, который не является одним из типов по умолчанию, поэтому добавление init(from decoder: Decoder) с настройками будет обходным путем для получения ожидаемого словаря.

struct Person: Codable {
    var name: String
    var history: [Int: String]

    init() {
        self.name = "Name"
        history = [0: "Test"]
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Failed to decode name"
        self.history = try container.decodeIfPresent([Int: String].self, forKey: .history) ?? [-1: "Failed to decode history"]
    }
}

let person = Person()
do {
    let jsonData = try JSONEncoder().encode(person)

    if let result = try? JSONDecoder().decode(Person.self, from: jsonData) {
        print(result)
    }
} catch {
    print(error.localizedDescription)    
}
person Lawliet    schedule 20.07.2017
comment
Спасибо, Лоулит, вы совершенно правы в том, что я могу декодировать его в человека, мне даже не нужно переопределять инициализацию из декодера, потому что на самом деле словарь анализируется просто отлично. - person Denis Balko; 20.07.2017

Оказывается, это словарь, он просто печатается так, как будто это не словарь. Причина, по которой я решил, что это не словарь, заключалась в том, что вызов

let history = dictionary["history"] as? [Int: String] 

приведет к нулю, но причина в том, что JSON, по-видимому, не обрабатывает [Int: String]. Вместо этого мой [Int: String] был превращен (вероятно, функцией JSONSerialization.jsonObject(with: jsonData, options: [])) в [String: Any].

let history = dictionary["history"] as? [String: String]

работает просто отлично.

Я до сих пор не понимаю, почему консоль печатает как с фигурными скобками, может быть, это ошибка?

person Denis Balko    schedule 20.07.2017