【问题标题】:How to resolve the typeMismatch error in SwiftUI?如何解决 SwiftUI 中的 typeMismatch 错误?
【发布时间】:2022-09-22 22:17:01
【问题描述】:

这是我的模型。当我获取详细信息时,发生了错误。

import Foundation

// MARK: - PublisherModel
public struct PublisherModel: Decodable {
    public let userslist: [UserslistModel]

    public init(userslist: [UserslistModel]) {
        self.userslist = userslist
    }
}

// MARK: - UserslistModel
public struct UserslistModel: Decodable, Hashable {
    public var id: String
    public let full_name: String
    public let totalBookViews: String
    public let totalBooks: String?
    public let full_address: String?
    private enum CodingKeys: String, CodingKey {
            case id,full_name,totalBookViews,totalBooks,full_address
        }
    public init(id: String, full_name: String, totalBookViews: String?, totalBooks: String?, full_address: String?) {
        self.id = id
        self.full_name = full_name
        self.totalBookViews = totalBookViews!
        self.totalBooks = totalBooks!
        self.full_address = full_address!
    }
    
   public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
       full_name = try container.decode(String.self, forKey: .full_name)
            full_address = try container.decode(String.self, forKey: .full_address)
       totalBooks = try container.decode(String.self, forKey: .totalBooks)
       totalBookViews = try container.decode(String.self, forKey: .totalBookViews)
            do {
                id = try String(container.decode(Int.self, forKey: .id))
            } catch DecodingError.typeMismatch {
                id = try container.decode(String.self, forKey: .id)
                
            }
        }
}

这是我的视图模型

class PublisherModelVM: ObservableObject {
    @Published var datas = [UserslistModel]()
   
    let url = \"https://alibrary.in/api/publisherList\"
    
    init() {
        getData(url: url)
    }
    
    func getData(url: String) {
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            if let data = data {
                do {
                    let results = try JSONDecoder().decode(PublisherModel.self, from: data).userslist
                    DispatchQueue.main.async {
                        self.datas = results.reversed()
                    }
                }
                catch {
                    print(error)
                }
            }
        }.resume()
    }
    
}

这是我的视图,我获取详细信息

struct PublisherDataView: View{
    @StateObject var list = PublisherModelVM()
    @State var logourl: String?
    var body: some View{
            ScrollView(.horizontal,showsIndicators: false){
                HStack{
                    ForEach( list.datas, id: \\.id){ item in

                        
                        VStack(spacing: 12){
                              Text(item.full_name )
                        Text(item.full_address ?? \"-\")
                        Text(item.totalBookViews)
                        Text(item.totalBooks!)
                        }
                        .frame(width:275, height: 220)
                        .background(Color.brown.opacity(0.5)).cornerRadius(12)
                        
                    
                }
            }
        }
    }
}

当运行此代码时,错误显示为:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: \"userslist\", intValue: nil), _JSONKey(stringValue: \"Index 2\", intValue: 2), CodingKeys(stringValue: \"totalBookViews\", intValue: nil)], debugDescription: \"Expected to decode String but found a number instead.\", underlyingError: nil))

我在哪里做错了?

每当我用 Int 更改 totalBookViews 时,就会出现这个新错误:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: \"userslist\", intValue: nil), _JSONKey(stringValue: \"Index 0\", intValue: 0), CodingKeys(stringValue: \"id\", intValue: nil)], debugDescription: \"Expected to decode String but found a number instead.\", underlyingError: nil))

我哪里错了?是不是每当我用字符串更改full_address 时?然后也出现了这个问题:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: \"userslist\", intValue: nil), _JSONKey(stringValue: \"Index 0\", intValue: 0), CodingKeys(stringValue: \"totalBooks\", intValue: nil)], debugDescription: \"Expected to decode String but found a number instead.\", underlyingError: nil))
  • 错误消息中的解释就在您面前,您已将 totalBookViews 声明为字符串,但它是 json 中的数字(Int 或 Double,但很可能是 Int)。在最坏的情况下,它既可以是字符串也可以是数字,但这将是一种糟糕的 json 格式,尽管也有解决方案。
  • @JoakimDanielson 请在我的 API 中查看我的 API totalBookViews 是 String 类型,但我尝试使用 Int 类型但新问题生成
  • 看起来它像我担心的那样混合在一起,所以你需要将它存储为一个字符串。
  • 你需要编写一个自定义的init(from:),这样你就可以处理这个糟糕的 json。请参阅this question 的已接受答案,以了解解决 int/string 问题的方法。
  • 另一件事 full_address 在响应中显示 null,因此将该属性声明为可选 public let full_address: String?

标签: json swift


【解决方案1】:

首先,请删除结构中的所有 init 方法。编译器代表您创建它们,Codable 无论如何都不会使用它们。

public init(userslist: [UserslistModel]) {
    self.userslist = userslist
}
    public init(id: Int, full_name: String, totalBookViews: String, totalBooks: Int, full_address: String) {
    self.id = id
    self.full_name = full_name
    self.totalBookViews = totalBookViews
    self.totalBooks = totalBooks
    self.full_address = full_address
}

其次,添加convertFromSnakeCase 策略将snake_case 键映射到camelCase 结构成员。

totalBookViews 有时是 Int,有时是 String。更复杂的是:字符串值不能直接转换为Int,因为它包含字母和空白字符。

您必须有条件地实现init(from decoder 并解码totalBookViews。如果是字符串,过滤数字字符并将字符串转换为Int

Nirav D 已经提到的另一个问题是fullAddress 可以是nilinit 方法也必须考虑到这一点。

// MARK: - PublisherModel
public struct PublisherModel: Decodable {
    public let userslist: [UserslistModel]
}

// MARK: - UserslistModel
public struct UserslistModel: Decodable {
    public let id: Int
    public let fullName: String
    public let totalBookViews: Int
    public let totalBooks: Int
    public let fullAddress: String
    
    private enum CodingKeys: String, CodingKey {
        case id, fullName, totalBookViews, totalBooks, fullAddress
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.fullName = try container.decode(String.self, forKey: .fullName)
        do {
            self.totalBookViews = try container.decode(Int.self, forKey: .totalBookViews)
        } catch {
            let bookViews = try container.decode(String.self, forKey: .totalBookViews).filter(\.isNumber)
            guard let bookViewsAmount = Int(bookViews) else {
                throw DecodingError.dataCorruptedError(forKey: .totalBookViews, in: container, debugDescription: "Wrong String Format")
            }
            self.totalBookViews = bookViewsAmount
        }
        
        self.totalBooks = try container.decode(Int.self, forKey: .totalBooks)
        self.fullAddress = try container.decodeIfPresent(String.self, forKey: .fullAddress) ?? ""
    }
}   

并创建解码器

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode(PublisherModel.self, from: data).userslist

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    • 2021-10-26
    • 2014-01-29
    • 2017-10-06
    • 2020-11-01
    • 2016-12-26
    相关资源
    最近更新 更多