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

SwiftUI WikiVoyage API数据模型JSON解析失败求助

SwiftUI WikiVoyage API JSON解析invalidData错误排查

已成功发起WikiVoyage API网络请求,但解析返回JSON时触发invalidData错误,目标是获取extract字段中的HTML字符串。


Proxyman捕获的响应体

{  
  "batchcomplete": true,  
  "warnings": {  
    "extracts": {  
      "warnings": "HTML may be malformed and/or unbalanced and may omit inline images. Use at your own risk. Known problems are listed at https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:TextExtracts#Caveats."  
    }  
  },  
  "query": {  
    "pages": [  
      {  
        "pageid": 5125,  
        "ns": 0,  
        "title": "Buriram",  
        "index": 1,  
        "thumbnail": {  
          "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Train_Time_Table_at_Buriram_Station..jpg/250px-Train_Time_Table_at_Buriram_Station..jpg",  
          "width": 250,  
          "height": 188  
        },  
        "extract": "HTML string here"  
      }  
    ]  
  }  
}

数据模型代码

import Foundation

struct WikiVoyageResponse: Codable {    
    let query: Query
}

struct Query: Codable {    
    let pages: [Page]
}

struct Page: Codable {    
    let pageid: Int    
    let ns: Int    
    let title: String    
    let index: Int    
    let thumbnail: Thumbnail    
    let extract: String
}

struct Thumbnail: Codable {    
    let source: String    
    let width: Int    
    let height: Int
}

API调用代码

func getWikiData() async throws -> String {        
    // 浏览器中可正常获取数据
    let urlString = "https://en.wikivoyage.org/w/api.php?origin=*&action=query&format=json&prop=pageimages%7Cextracts&exlimit=1&generator=prefixsearch&redirects=1&formatversion=2&piprop=thumbnail&pithumbsize=250&pilimit=20&gpssearch=Buriram&gpslimit=1"        
    
    guard let url = URL(string: urlString) else {            
        throw DataError.invalidURL            
        // 通知用户
    }        
    
    let (data, response) = try await URLSession.shared.data(from: url)        
    
    guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {            
        throw DataError.invalidResponse            
        // 通知用户
    }       
    
    do {            
        let decoder = JSONDecoder()            
        let response = try decoder.decode(WikiVoyageResponse.self, from: data)            
        
        // 期望获取extract中的HTML字符串,但触发invalidData错误
        print("Response", response.query.pages[0].extract)            
        return response.query.pages[0].extract            
    } catch {            
        throw DataError.invalidData        
    }        
}

问题排查与解决方案

1. 模型未处理响应中的额外字段

响应包含batchcomplete和warnings字段,但当前模型未定义这些字段,JSONDecoder默认会因存在未知字段抛出错误。解决方式二选一:

  • 添加可选字段:扩展模型结构,将额外字段标记为可选
    struct WikiVoyageResponse: Codable {    
        let query: Query
        let batchcomplete: Bool?
        let warnings: Warnings?
    }
    
    struct Warnings: Codable {
        let extracts: ExtractWarning?
    }
    
    struct ExtractWarning: Codable {
        let warnings: String?
    }
    
  • 配置解码器忽略未知字段:修改解码器设置,跳过模型中未定义的字段
    let decoder = JSONDecoder()
    decoder.ignoresUnknownKeys = true // 关键设置
    let response = try decoder.decode(WikiVoyageResponse.self, from: data)
    

2. 非可选字段可能为nil

部分场景下API可能不返回thumbnail字段,当前模型中thumbnail为非可选类型,会导致解析失败,建议改为可选:

struct Page: Codable {    
    let pageid: Int    
    let ns: Int    
    let title: String    
    let index: Int    
    let thumbnail: Thumbnail? // 改为可选
    let extract: String
}

3. 检查原始JSON格式

若上述调整后仍报错,可打印原始响应字符串,排查是否存在转义错误或格式问题:

if let rawJson = String(data: data, encoding: .utf8) {
    print("原始响应字符串:\(rawJson)")
}

内容的提问来源于stack exchange,提问作者Mike Haslam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 03:12:43