You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:38:29