使用Vapor Swift解码XML请求内容失败,求正确实现方法
在Vapor中正确解码XML请求体的方法
问题场景
我正在尝试在Vapor中解码带有XML请求体的请求,请求内容如下:
<?xml version='1.0' encoding='UTF-8'?> <feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns="http://www.w3.org/2005/Atom"><link rel="hub" href="https://pubsubhubbub.appspot.com"/><link rel="self" href="https://www.youtube.com/xml/feeds/videos.xml?channel_id=REDACTED"/><title>YouTube video feed</title><updated>2024-11-27T15:08:44.561926689+00:00</updated><entry> <id>yt:video:REDACTED</id> <yt:videoId>REDACTED</yt:videoId> <yt:channelId>REDACTED</yt:channelId> <title>Test Video</title> <link rel="alternate" href="https://www.youtube.com/watch?v=REDACTED"/> <author> <name>Test User</name> <uri>https://www.youtube.com/channel/REDACTED</uri> </author> <published>2024-11-27T15:08:41+00:00</published> <updated>2024-11-27T15:08:44.561926689+00:00</updated> </entry></feed>
我尝试将其解码为以下模型:
struct PubSub: Content { struct Feed: Codable { struct Entry: Codable { let id: String } let entry: Entry } let feed: Feed }
使用的解码代码:
guard let pubSub = try? req.content.decode(PubSub.self, as: .xml) else { throw Abort(.badGateway) } return pubSub
但解码始终失败,请问在Vapor中解码XML的正确方式是什么?
问题原因与解决方法
解码失败的核心原因是XML命名空间处理和Codable键映射的问题,以下是具体修正步骤:
1. 处理XML默认命名空间
你的XML根元素<feed>带有默认命名空间xmlns="http://www.w3.org/2005/Atom",Vapor的XML解码器默认不会自动处理命名空间,需要显式指定映射关系。
2. 修正Codable模型的键映射
需要通过CodingKeys枚举匹配XML元素名,若要解析带yt:前缀的元素,还需对应处理其命名空间。
修正后的模型代码
import Vapor struct PubSub: Content { struct Feed: Codable { struct Entry: Codable { let id: String // 若需要解析yt前缀字段,可取消以下注释 // let videoId: String // let channelId: String enum CodingKeys: String, CodingKey { case id // 对应yt命名空间的字段名 // case videoId = "videoId" // case channelId = "channelId" } // 自定义初始化器处理命名空间 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: .id) // 解析yt前缀字段的逻辑(可选) // guard let namespaceMap = decoder.userInfo[.xmlNamespaceKey] as? [String: String], // let ytURI = namespaceMap["yt"] else { // throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "缺少yt命名空间定义")) // } // let ytContainer = try decoder.container(keyedBy: CodingKeys.self, forNamespace: ytURI) // videoId = try ytContainer.decode(String.self, forKey: .videoId) // channelId = try ytContainer.decode(String.self, forKey: .channelId) } } let entry: Entry enum CodingKeys: String, CodingKey { case entry } } let feed: Feed enum CodingKeys: String, CodingKey { case feed } }
3. 正确的解码实现
解码时需将XML命名空间信息传递给解码器,同时避免用try?隐藏错误:
// 定义XML命名空间映射 let namespaceMap = [ "": "http://www.w3.org/2005/Atom", // 默认命名空间 "yt": "http://www.youtube.com/xml/schemas/2015" ] // 创建自定义XML解码器并传入命名空间 var xmlDecoder = XMLDecoder() xmlDecoder.userInfo[.xmlNamespaceKey] = namespaceMap do { let pubSub = try req.content.decode(PubSub.self, using: xmlDecoder) return pubSub } catch { print("XML解码错误详情: \(error)") throw Abort(.badRequest, reason: "XML格式解析失败") }
额外注意事项
- 不要用
try?屏蔽错误,改用do-catch捕获具体错误信息,方便排查问题。 - 如果XML中可能存在多个
<entry>元素,需将模型中的entry改为[Entry]数组类型。 - Vapor的XML解码基于
XMLDecoder,完全遵循Swift的Codable协议,自定义解码逻辑和常规Codable处理一致。
内容的提问来源于stack exchange,提问作者Boryk
相关产品推荐
相关产品推荐

