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

为带嵌套关联值的类型实现自定义Decodable初始化报错求助

自定义Decodable协议init方法时遇到解码错误

错误信息

DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Foundation.JSONValue>)
Expected to decode Dictionary<String, JSONValue> but found a string instead.

问题代码

struct Model: Decodable {
  let title: String
  let gotoAction: GotoAction // String work fine
}

enum GotoAction: Decodable {
  case noAction
  case websiteLink(String)
  case sheet(OpenSheet)

  private enum CodingKeys: String, CodingKey {
    case noAction
    case websiteLink
    case sheet
  }

   init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

       if let link = try container.decodeIfPresent(String.self, forKey: .websiteLink) {
        self = .websiteLink(link)
       }

       if let sheet = try container.decodeIfPresent(OpenSheet.self, forKey: .sheet) {
        self = .sheet(sheet)
       }

      //  let noAction = try container.decodeIfPresent(String.self, forKey: .noAction)
      //  self = noAction

      throw DecodingError.dataCorruptedError(forKey: .websiteLink, in: container, debugDescription: "No match")
    }
}

enum OpenSheet: Decodable {
  case firstBanner
  case secondBanner(String)
}

let json = """
{
  "title": "Hello",
  "goto_action": "no_action"
}
"""

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let result = try decoder.decode(Model.self, from: json.data(using: .utf8)!)
print(result)

问题分析与修复

核心问题有两个:

  1. GotoAction的解码逻辑错误假设goto_action是键值对结构,但实际JSON中它是单个字符串,调用decoder.container(keyedBy: CodingKeys.self)会触发类型不匹配错误。
  2. 未正确处理noAction的解码逻辑,且无论匹配结果如何都会抛出错误。

修改步骤:

1. 调整GotoAction解码逻辑适配字符串类型

因为goto_action是单个字符串值,直接用单值容器解码后匹配枚举case:

enum GotoAction: Decodable {
  case noAction
  case websiteLink(String)
  case sheet(OpenSheet)

  init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    
    // 先解析字符串类型的case
    let rawValue = try container.decode(String.self)
    switch rawValue {
    case "no_action":
      self = .noAction
    case let link where link.hasPrefix("http"): // 根据实际规则判断网站链接
      self = .websiteLink(link)
    default:
      // 尝试解析OpenSheet类型(需根据实际JSON格式调整)
      do {
        let sheet = try container.decode(OpenSheet.self)
        self = .sheet(sheet)
      } catch {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported goto action: \(rawValue)")
      }
    }
  }
}

2. 完善OpenSheet的解码逻辑

如果OpenSheet对应JSON是字符串格式,自定义解码逻辑:

enum OpenSheet: Decodable {
  case firstBanner
  case secondBanner(String)

  init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let rawValue = try container.decode(String.self)
    
    if rawValue == "first_banner" {
      self = .firstBanner
    } else if rawValue.starts(with: "second_banner:") {
      let content = String(rawValue.dropFirst("second_banner:".count))
      self = .secondBanner(content)
    } else {
      throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported sheet type: \(rawValue)")
    }
  }
}

3. 兼容混合格式(可选)

如果goto_action有时是字符串、有时是对象,可先判断容器类型:

init(from decoder: Decoder) throws {
  if let singleContainer = try? decoder.singleValueContainer() {
    // 处理字符串类型
    let rawValue = try singleContainer.decode(String.self)
    switch rawValue {
    case "no_action":
      self = .noAction
    case let link where link.hasPrefix("http"):
      self = .websiteLink(link)
    default:
      throw DecodingError.dataCorruptedError(in: singleContainer, debugDescription: "Unsupported action string: \(rawValue)")
    }
  } else if let keyedContainer = try? decoder.container(keyedBy: CodingKeys.self) {
    // 处理键值对结构(比如{"sheet": "first_banner"})
    if let sheet = try keyedContainer.decodeIfPresent(OpenSheet.self, forKey: .sheet) {
      self = .sheet(sheet)
    } else if let link = try keyedContainer.decodeIfPresent(String.self, forKey: .websiteLink) {
      self = .websiteLink(link)
    } else {
      throw DecodingError.dataCorruptedError(forKey: .websiteLink, in: keyedContainer, debugDescription: "No valid action found")
    }
  } else {
    throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON structure for GotoAction"))
  }
}

内容的提问来源于stack exchange,提问作者Nick Rossik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 11:15:22