web-dev-qa-db-fra.com

Implémentation d'un décodeur personnalisé dans Swift 4

Je voudrais décoder un document XML en utilisant le nouveau protocole Decodable introduit dans Swift 4, cependant, il ne semble pas y avoir d'implémentation existante pour un décodeur XML qui est conforme au protocole Decoder.

Mon plan était d'utiliser la bibliothèque SWXMLHash pour analyser le XML, puis de faire éventuellement de la classe XMLIndexer dans cette bibliothèque étendre le protocole Decoder afin que mon modèle puisse être initialisé avec une instance de XMLIndexer (XMLIndexer est retourné par SWXMLHash.parse(xmlString)).

XMLIndexer+Decoder.Swift

Mon problème est que je n'ai aucune idée de comment implémenter le protocole Decoder et je n'arrive pas à trouver de ressources en ligne qui expliquent comment cela est fait. Chaque ressource que j'ai trouvée mentionne strictement la classe JSONDecoder qui est incluse avec la bibliothèque standard Swift et aucune ressource que j'ai trouvée résout le problème de la création de votre propre décodeur personnalisé .

27
Toni Sučić

Je n'ai pas encore eu la chance de transformer mon code en framework, mais vous pouvez jeter un œil à mon référentiel Github qui implémente à la fois un décodeur et un encodeur personnalisés pour XML.

Lien: https://github.com/ShawnMoore/XMLParsing

L'encodeur et le décodeur résident dans le dossier XML du dépôt. Il est basé sur JSONEncoder et JSONDecoder d'Apple avec des modifications pour s'adapter à la norme XML.


Différences entre XMLDecoder et JSONDecoder

  1. XMLDecoder.DateDecodingStrategy a un cas supplémentaire intitulé keyFormatted. Ce cas prend une fermeture qui vous donne une CodingKey, et c'est à vous de fournir le DateFormatter correct pour la clé fournie. Il s'agit simplement d'un cas pratique sur le DateDecodingStrategy de JSONDecoder.
  2. XMLDecoder.DataDecodingStrategy a un cas supplémentaire intitulé keyFormatted. Ce cas prend une fermeture qui vous donne une CodingKey, et c'est à vous de fournir les données correctes ou nulles pour la clé fournie. Il s'agit simplement d'un cas pratique sur le DataDecodingStrategy de JSONDecoder.
  3. Si l'objet conforme au protocole Codable possède un tableau et que le XML analysé ne contient pas l'élément de tableau, XMLDecoder affectera un tableau vide à l'attribut. En effet, la norme XML indique que si le XML ne contient pas l'attribut, cela pourrait signifier qu'il n'y a aucun de ces éléments.

Différences entre XMLEncoder et JSONEncoder

  1. Contient une option appelée StringEncodingStrategy, cette énumération a deux options, deferredToString et cdata. L'option deferredToString est par défaut et encodera les chaînes comme de simples chaînes. Si cdata est sélectionné, toutes les chaînes seront encodées en CData.

  2. La fonction encode accepte deux paramètres supplémentaires que JSONEncoder. Le premier paramètre supplémentaire dans la fonction est une chaîne RootKey qui aura le XML entier enveloppé dans un élément nommé cette clé. Ce paramètre est obligatoire. Le deuxième paramètre est un XMLHeader, qui est un paramètre facultatif qui peut prendre la version, la stratégie d'encodage et l'état autonome, si vous souhaitez inclure ces informations dans le xml encodé.


Exemples

Pour une liste complète d'exemples, consultez le dossier Sample XML dans le référentiel.

XML à analyser:

<?xml version="1.0"?>
<book id="bk101">
    <author>Gambardella, Matthew</author>
    <title>XML Developer's 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>

Structures rapides:

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"
}

Décodeur XML:

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)
}
32
S.Moore