web-dev-qa-db-ja.com

Swift 4でカスタムデコーダーを実装する

Swift 4で導入された新しいDecodableプロトコルを使用してXMLドキュメントをデコードしたいのですが、XMLデコーダーの既存の実装はないようです。 Decoderプロトコルに準拠しています。

私の計画は、SWXMLHashライブラリを使用してXMLを解析し、そのライブラリのXMLIndexerクラスでDecoderプロトコルを拡張して、モデルをXMLIndexerXMLIndexerSWXMLHash.parse(xmlString)によって返されます)。

XMLIndexer+Decoder.Swift

私の問題は、Decoderプロトコルの実装方法がわからず、その方法を説明するリソースがオンラインで見つからないように見えることです。私が見つけたすべてのリソースは、Swift標準ライブラリに含まれているJSONDecoderクラスに厳密に言及していますが、独自のカスタムデコーダを作成する問題に対処しているリソースはありません。

27
Toni Sučić

コードをフレームワークに変える機会はまだありませんが、XMLのカスタムデコーダーとエンコーダーの両方を実装するGithubリポジトリをご覧ください。

リンク: https://github.com/ShawnMoore/XMLParsing

エンコーダーとデコーダーは、リポジトリのXMLフォルダーにあります。 AppleのJSONEncoderおよびJSONDecoderに基づいており、XML標準に適合するように変更されています。


XMLDecoderとJSONDecoderの違い

  1. XMLDecoder.DateDecodingStrategyにはkeyFormattedというタイトルの追加のケースがあります。このケースでは、CodingKeyを提供するクロージャーが必要です。提供されたキーに正しいDateFormatterを提供するのはユーザー次第です。これは、JSONDecoderのDateDecodingStrategyの便利なケースです。
  2. XMLDecoder.DataDecodingStrategyにはkeyFormattedというタイトルの追加のケースがあります。この場合、CodingKeyを提供するクロージャーが必要です。正しいデータを提供するか、提供されたキーにnilを提供するかはユーザー次第です。これは、JSONDecoderのDataDecodingStrategyの便利なケースです。
  3. Codableプロトコルに準拠するオブジェクトに配列があり、解析対象のXMLに配列要素が含まれていない場合、XMLDecoderは属性に空の配列を割り当てます。これは、XML標準がXMLに属性が含まれていない場合、それらの要素がゼロであることを意味する可能性があるためです。

XMLEncoderとJSONEncoderの違い

  1. StringEncodingStrategyというオプションが含まれています。この列挙型には、deferredToStringcdataの2つのオプションがあります。 deferredToStringオプションはデフォルトであり、文字列を単純な文字列としてエンコードします。 cdataが選択されている場合、すべての文字列はCDataとしてエンコードされます。

  2. encode関数は、JSONEncoderよりも2つの追加パラメーターを受け取ります。関数の最初の追加パラメーターはRootKey文字列で、そのキーという名前の要素にXML全体がラップされます。このパラメーターは必須です。 2番目のパラメーターはXMLHeaderです。これは、エンコードされたxmlにこの情報を含める場合、バージョン、エンコード戦略、スタンドアロンステータスを取得できるオプションのパラメーターです。


サンプルの完全なリストについては、リポジトリ内のSample XMLフォルダーを参照してください。

解析するXML:

<?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>

スイフト構造:

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