Swift 4で導入された新しいDecodable
プロトコルを使用してXMLドキュメントをデコードしたいのですが、XMLデコーダーの既存の実装はないようです。 Decoder
プロトコルに準拠しています。
私の計画は、SWXMLHashライブラリを使用してXMLを解析し、そのライブラリのXMLIndexer
クラスでDecoder
プロトコルを拡張して、モデルをXMLIndexer
(XMLIndexer
はSWXMLHash.parse(xmlString)
によって返されます)。
私の問題は、Decoder
プロトコルの実装方法がわからず、その方法を説明するリソースがオンラインで見つからないように見えることです。私が見つけたすべてのリソースは、Swift標準ライブラリに含まれているJSONDecoder
クラスに厳密に言及していますが、独自のカスタムデコーダを作成する問題に対処しているリソースはありません。
コードをフレームワークに変える機会はまだありませんが、XMLのカスタムデコーダーとエンコーダーの両方を実装するGithubリポジトリをご覧ください。
リンク: https://github.com/ShawnMoore/XMLParsing
エンコーダーとデコーダーは、リポジトリのXMLフォルダーにあります。 AppleのJSONEncoderおよびJSONDecoderに基づいており、XML標準に適合するように変更されています。
XMLDecoderとJSONDecoderの違い
XMLDecoder.DateDecodingStrategy
にはkeyFormatted
というタイトルの追加のケースがあります。このケースでは、CodingKeyを提供するクロージャーが必要です。提供されたキーに正しいDateFormatterを提供するのはユーザー次第です。これは、JSONDecoderのDateDecodingStrategyの便利なケースです。XMLDecoder.DataDecodingStrategy
にはkeyFormatted
というタイトルの追加のケースがあります。この場合、CodingKeyを提供するクロージャーが必要です。正しいデータを提供するか、提供されたキーにnilを提供するかはユーザー次第です。これは、JSONDecoderのDataDecodingStrategyの便利なケースです。XMLEncoderとJSONEncoderの違い
StringEncodingStrategy
というオプションが含まれています。この列挙型には、deferredToString
とcdata
の2つのオプションがあります。 deferredToStringオプションはデフォルトであり、文字列を単純な文字列としてエンコードします。 cdataが選択されている場合、すべての文字列はCDataとしてエンコードされます。
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)
}