Swift4中用SWXMLHash+Alamofire解析XML报错,求实体映射方案
Hey there! Let's tackle your problem step by step—first fixing that Sequence conformance error, then showing you how to map the XML entries into a struct for cleaner, more maintainable code.
Type 'XMLIndexer' does not conform to protocol 'Sequence' Error The root issue here is that in SWXMLHash 4.x, the result of xml["feed"]["entry"] is an XMLIndexer type, which doesn't directly conform to the Sequence protocol (so you can't loop over it directly with a for-in). To access the list of entry nodes, you need to use the all property of the XMLIndexer to get a traversable collection.
Here's the corrected request code:
Alamofire.request("https://itunes.apple.com/us/rss/topgrossingapplications/limit=10/xml").response { response in debugPrint(response) guard let data = response.data else { return } let xml = SWXMLHash.parse(data) // Access the `all` property to get a sequence of entry nodes let entryNodes = xml["feed"]["entry"].all for node in entryNodes { print(node["title"].text) } }
To make your code more organized, you can map each XML entry tag to a custom struct. SWXMLHash provides the XMLIndexerDeserializable protocol to simplify this process.
Step 1: Define the Struct
First, create a struct that matches the data you want to extract from the XML. Below is an example that captures common fields from the iTunes RSS feed:
import SWXMLHash struct AppEntry: XMLIndexerDeserializable { let title: String let appId: String let artistName: String let releaseDate: String let appUrl: String static func deserialize(_ node: XMLIndexer) throws -> AppEntry { return try AppEntry( title: node["title"].value(), appId: node["id"]["attributes"]["im:id"].value(), artistName: node["im:artist"].value(), releaseDate: node["im:releaseDate"]["attributes"]["label"].value(), appUrl: node["link"]["attributes"]["href"].value() ) } }
Step 2: Parse Directly to Struct Array
Now you can modify your request code to parse all entry nodes directly into an array of AppEntry instances. This uses SWXMLHash's built-in deserialization to handle the mapping automatically:
Alamofire.request("https://itunes.apple.com/us/rss/topgrossingapplications/limit=10/xml").response { response in debugPrint(response) guard let data = response.data else { return } let xml = SWXMLHash.parse(data) do { // Deserialize all entry nodes into an array of AppEntry let appEntries: [AppEntry] = try xml["feed"]["entry"].value() // Use the parsed data for app in appEntries { print("App: \(app.title) by \(app.artistName) | ID: \(app.appId)") } } catch { print("Parsing error: \(error.localizedDescription)") } }
Notes for Optional Fields
If some fields in the XML might be missing, you can handle them as optional values in your struct. For example:
struct AppEntry: XMLIndexerDeserializable { let title: String let appId: String let artistName: String? // Optional field static func deserialize(_ node: XMLIndexer) throws -> AppEntry { return AppEntry( title: try node["title"].value(), appId: try node["id"]["attributes"]["im:id"].value(), artistName: node["im:artist"].value() // No `try` here since it's optional ) } }
内容的提问来源于stack exchange,提问作者Asif Raza

