是否可以使用JSONDecoder解码其他参数?

编程入门 行业动态 更新时间:2024-10-23 06:25:49
本文介绍了是否可以使用JSONDecoder解码其他参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

后端有一些回复:

{ "name": "Some name", "number": 42, ............ "param0": value0, "param1": value1, "param2": value2 }

响应的模型结构:

struct Model: Codable { let name: String let number: Int let params: [String: Any] }

如何使JSONDecoder将所有未知的键值对组合到params属性中?

How to make JSONDecoder combine all unknown key-value pairs into params property?

推荐答案

Decodable非常强大.它可以解码完全任意的JSON,因此这只是该问题的一个子集.有关完全解决的JSON Decodable,请参见此 JSON .

Decodable is incredibly powerful. It can decode completely arbitrary JSON, so this is just a sub-set of that problem. For a fully worked-out JSON Decodable, see this JSON.

我将从示例中提取Key的概念,但为简单起见,我将假定值必须为Int或String.您可以将parameters设置为[String: JSON],并改用我的JSON解码器.

I'll pull the concept of Key from example, but for simplicity I'll assume that values must be either Int or String. You could make parameters be [String: JSON] and use my JSON decoder instead.

struct Model: Decodable { let name: String let number: Int let params: [String: Any] // An arbitrary-string Key, with a few "well known and required" keys struct Key: CodingKey, Equatable { static let name = Key("name") static let number = Key("number") static let knownKeys = [Key.name, .number] static func ==(lhs: Key, rhs: Key) -> Bool { return lhs.stringValue == rhs.stringValue } let stringValue: String init(_ string: String) { self.stringValue = string } init?(stringValue: String) { self.init(stringValue) } var intValue: Int? { return nil } init?(intValue: Int) { return nil } } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Key.self) // First decode what we know name = try container.decode(String.self, forKey: .name) number = try container.decode(Int.self, forKey:. number) // Find all the "other" keys let optionalKeys = container.allKeys .filter { !Key.knownKeys.contains($0) } // Walk through the keys and try to decode them in every legal way // Throw an error if none of the decodes work. For this simple example // I'm assuming it is a String or Int, but this is also solvable for // arbitarily complex data (it's just more complicated) // This code is uglier than it should be because of the `Any` result. // It could be a lot nicer if parameters were a more restricted type var p: [String: Any] = [:] for key in optionalKeys { if let stringValue = try? container.decode(String.self, forKey: key) { p[key.stringValue] = stringValue } else { p[key.stringValue] = try container.decode(Int.self, forKey: key) } } params = p } } let json = Data(""" { "name": "Some name", "number": 42, "param0": 1, "param1": "2", "param2": 3 } """.utf8) try JSONDecoder().decode(Model.self, from: json) // Model(name: "Some name", number: 42, params: ["param0": 1, "param1": "2", "param2": 3])

其他想法

ADDITIONAL THOUGHTS

我认为以下评论非常重要,未来的读者应该仔细阅读.我想展示需要很少的代码重复,并且可以轻松地提取和重用其中的多少代码,这样就不需要魔术或动态功能.

I think the comments below are really important and future readers should look them over. I wanted to show how little code duplication is required, and how much of this can be easily extracted and reused, such that no magic or dynamic features are required.

首先,提取常见且可重复使用的片段:

First, extract the pieces that are common and reusable:

func additionalParameters<Key>(from container: KeyedDecodingContainer<Key>, excludingKeys: [Key]) throws -> [String: Any] where Key: CodingKey { // Find all the "other" keys and convert them to Keys let excludingKeyStrings = excludingKeys.map { $0.stringValue } let optionalKeys = container.allKeys .filter { !excludingKeyStrings.contains($0.stringValue)} var p: [String: Any] = [:] for key in optionalKeys { if let stringValue = try? container.decode(String.self, forKey: key) { p[key.stringValue] = stringValue } else { p[key.stringValue] = try container.decode(Int.self, forKey: key) } } return p } struct StringKey: CodingKey { let stringValue: String init(_ string: String) { self.stringValue = string } init?(stringValue: String) { self.init(stringValue) } var intValue: Int? { return nil } init?(intValue: Int) { return nil } }

现在,Model的解码器简化为

struct Model: Decodable { let name: String let number: Int let params: [String: Any] init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringKey.self) name = try container.decode(String.self, forKey: StringKey("name")) number = try container.decode(Int.self, forKey: StringKey("number")) params = try additionalParameters(from: container, excludingKeys: ["name", "number"].map(StringKey.init)) } }

如果有某种神奇的方式说请以默认方式处理这些属性",那将是很好的选择,但是我不太清楚坦率地说是什么样子.这里的代码量与实现NSCoding大约相同,并且比针对NSJSONSerialization的实现少得多,并且如果代码过于繁琐,则很容易将其交给swiftgen(基本上,这是您必须为).作为交换,我们得到了完整的编译时类型检查,因此我们知道当我们遇到意外情况时它不会崩溃.

It would be nice if there were some magic way to say "please take care of these properties in the default way," but I don't quite know what that would look like frankly. The amount of code here is about the same as for implementing NSCoding, and much less than for implementing against NSJSONSerialization, and is easily handed to swiftgen if it were too tedious (it's basically the code you have to write for init). In exchange, we get full compile-time type checking, so we know it won't crash when we get something unexpected.

有几种方法可以使上述内容更短一些(我目前正在考虑有关KeyPath的想法,以使其更加方便).关键是当前的工具非常强大,值得探索.

There are a few ways to make even the above a bit shorter (and I'm currently thinking about ideas involving KeyPaths to make it even more convenient). The point is that the current tools are very powerful and worth exploring.

更多推荐

是否可以使用JSONDecoder解码其他参数?

本文发布于:2023-11-26 17:59:42,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1634600.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:可以使用   参数   JSONDecoder

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!