Расшифровать JSON с помощью ключа динамического кодирования, используя Codable? - PullRequest
0 голосов
/ 13 ноября 2018

Пример Json мне нужно декодировать.В «текстовом» ключе мы имеем [String: String] dict.И количество элементов находится в «подсчете».Как мне правильно его декодировать?

{
"name": "LoremIpsum",
"index": "1",
"text": {
    "text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
    "text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
    "text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
    "text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
"count": "4"
}

Моя кодируемая модель:

class Text: Codable {

private enum CodingKeys: String, CodingKey {
    case name, index, count, text
}

public var name: String?
public var index: Int?
public var count: Int?
public var texts: [String]?

init() {
    name = ""
    index = 0
    count = 0
    texts = []
}

init(name: String,
     index: Int,
     count: Int,
     texts: [String]) {
    self.name = name
    self.index = index
    self.count = count
    self.texts = texts
}

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)

}    <---- also why do I need this method? 

required public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    self.name = try container.decode(String.self, forKey: .name)
    self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
    self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
    let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)

    for i in (1...self.count!) {
        self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_\(i)") ?? .text))
    }
}

}

И я декодирую это с помощью:

if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
            let jsonObj = try JSONDecoder().decode(Text.self, from: data)
            print("jsonData:\(jsonObj)")
        } catch let error {
            print("parse error: \(error.localizedDescription)")
        }
    } else {
        print("Invalid filename/path.")
    }

Но я получил ошибку разбора

ошибка синтаксического анализа: данные не могут быть прочитаны, поскольку они отсутствуют.

Что не так с моим кодом?Это хороший способ декодировать такие динамические ключи кодирования?

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

Вам нужно

struct Root: Codable {
    let name, index,count: String
    let text: [String:String]
}

-

let res = try? JSONDecoder().decode(Root.self,from:jsonData)
0 голосов
/ 13 ноября 2018

Самое важное, что вам нужно, это:

let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }

Это преобразует словарь в массив значений. Хотя в принципе значения JSON не упорядочены, упорядочено allKeys (потому что это сериализованный протокол).

Вам не понадобится метод кодирования, если вы соответствуете только Decodable, а не Codable. Кроме того, большинство ваших опций не являются опциональными.

Соединяя эти вещи, вы получите:

class Text: Decodable {

    private enum CodingKeys: String, CodingKey {
        case name, index, count, text
    }

    public var name: String
    public var index: Int
    public var count: Int
    public var texts: [String]

    init(name: String = "",
         index: Int = 0,
         count: Int = 0,
         texts: [String] = []) {
        self.name = name
        self.index = index
        self.count = count
        self.texts = texts
    }

    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.name = try container.decode(String.self, forKey: .name)
        self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
        self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
        let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
        texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
    }

}

let jsonObj = try JSONDecoder().decode(Text.self, from: data)
0 голосов
/ 13 ноября 2018

Fix JSON

Полагаю, вы хотите, чтобы ваш индекс и число были числами. Так замените это "index": "1" и это "count": "4" С этим "index": 1 и этим "count": 4

Структура класса

При использовании протокола Codable любой из этих CodingKeys и функции кодирования или требуемые блоки не являются необходимыми. Также измените формат данных свойства текстов на [String: String]

Итак, замените свой класс следующим образом:

class Text: Codable {

    public var name: String
    public var index: Int
    public var count: Int
    public var texts: [String: String]

    init(name: String, index: Int, count: Int, texts: [String: String]) {
        self.name = name
        self.index = index
        self.count = count
        self.texts = texts
    }

}

Декодирование

Для декодирования вашего json используйте то, что вы написали выше, это правильно

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