Подтвердить что ты не робот

Внедрение пользовательского декодера в Swift 4

Я хотел бы декодировать XML-документ с использованием нового протокола Decodable, представленного в Swift 4, однако, похоже, не существует существующей реализации для XML-декодера, который соответствует протоколу Decoder.

Мой план состоял в том, чтобы использовать библиотеку SWXMLHash для синтаксического анализа XML, а затем, возможно, сделать класс XMLIndexer в этой библиотеке, расширить протокол Decoder, чтобы моя модель была инициализирована экземпляром XMLIndexer (XMLIndexer возвращается SWXMLHash.parse(xmlString)).

XMLIndexer + Decoder.swift

Моя проблема в том, что я не знаю, как реализовать протокол Decoder, и я не могу найти какие-либо ресурсы в Интернете, которые объясняют, как это сделать. Каждый найденный мной ресурс строго упоминает класс JSONDecoder, который включен в стандартную библиотеку Swift, и никакой ресурс, который я нашел, не затрагивает проблему создания собственного пользовательского декодера.

4b9b3361

Ответ 1

У меня еще не было возможности превратить мой код в структуру, но вы можете взглянуть на мой репозиторий Github, который реализует как собственный декодер, так и кодировщик для XML.

Ссылка: https://github.com/ShawnMoore/XMLParsing

Кодер и декодер находятся в папке XML репо. Он основан на Apple JSONEncoder и JSONDecoder с изменениями, соответствующими стандарту XML.


Различия между XMLDecoder и JSONDecoder

  • XMLDecoder.DateDecodingStrategy имеет дополнительный регистр под названием keyFormatted. Этот случай принимает замыкание, которое дает вам CodingKey, и вам решать предоставить правильный DateFormatter для предоставленного ключа. Это просто удобный случай для DateDecodingStrategy JSONDecoder.
  • XMLDecoder.DataDecodingStrategy имеет дополнительный регистр под названием keyFormatted. Этот случай принимает замыкание, которое дает вам CodingKey, и вам решать предоставить правильные данные или нуль для предоставленного ключа. Это просто удобный случай для DataDecodingStrategy JSONDecoder.
  • Если объект, соответствующий протоколу Codable, имеет массив, а анализируемый XML не содержит элемент массива, XMLDecoder назначит пустой атрибут атрибуту. Это связано с тем, что в стандарте XML говорится, что XML не содержит атрибута, что может означать, что существует нуль этих элементов.

Различия между XMLEncoder и JSONEncoder

  • Содержит опцию StringEncodingStrategy, это перечисление имеет два варианта: deferredToString и cdata. Параметр deferredToString по умолчанию и будет кодировать строки как простые строки. Если выбран cdata, все строки будут закодированы как CData.

  • Функция encode принимает два дополнительных параметра, чем JSONEncoder. Первым дополнительным параметром в функции является строка RootKey, которая будет содержать весь XML, завернутый в элемент с именем этого ключа. Этот параметр необходим. Второй параметр - XMLHeader, который является необязательным параметром, который может принимать версию, стратегию кодирования и автономный статус, если вы хотите включить эту информацию в закодированный xml.


Примеры

Полный список примеров см. в папке Sample XML в репозитории.

XML для анализа:

<?xml version="1.0"?>
<book id="bk101">
    <author>Gambardella, Matthew</author>
    <title>XML Developer Guide</title>
    <genre>Computer</genre>
    <price>44.95</price>
    <publish_date>2000-10-01</publish_date>
    <description>An in-depth look at creating applications
        with XML.</description>
</book>

Swift Structs:

struct Book: Codable {
    var id: String
    var author: String
    var title: String
    var genre: Genre
    var price: Double
    var publishDate: Date
    var description: String

    enum CodingKeys: String, CodingKey {
        case id, author, title, genre, price, description

        case publishDate = "publish_date"
    }
}

enum Genre: String, Codable {
    case computer = "Computer"
    case fantasy = "Fantasy"
    case romance = "Romance"
    case horror = "Horror"
    case sciFi = "Science Fiction"
}

XMLDecoder:

let data = Data(forResource: "book", withExtension: "xml") else { return nil }

let decoder = XMLDecoder()

let formatter: DateFormatter = {
   let formatter = DateFormatter()
   formatter.dateFormat = "yyyy-MM-dd"
   return formatter
}()

decoder.dateDecodingStrategy = .formatted(formatter)

do {
   let book = try decoder.decode(Book.self, from: data)
} catch {
   print(error)
}

XMLEncoder:

let encoder = XMLEncoder()

let formatter: DateFormatter = {
   let formatter = DateFormatter()
   formatter.dateFormat = "yyyy-MM-dd"
   return formatter
}()

encoder.dateEncodingStrategy = .formatted(formatter)

do {
   let data = try encoder.encode(self, withRootKey: "book", header: XMLHeader(version: 1.0))

   print(String(data: data, encoding: .utf8))
} catch {
   print(error)
}