Swift:可编码 - 提取单个编码密钥

2024-04-22

我有以下代码来提取编码密钥中包含的 JSON:

let value = try! decoder.decode([String:Applmusic].self, from: $0["applmusic"])

这成功处理了以下 JSON:

{
  "applmusic":{
    "code":"AAPL",
    "quality":"good",
    "line":"She told me don't worry",
}

但是,无法提取编码密钥为的 JSONapplmusic来自以下一项:

{
  "applmusic":{
    "code":"AAPL",
    "quality":"good",
    "line":"She told me don't worry",
  },
  "spotify":{
    "differentcode":"SPOT",
    "music_quality":"good",
    "spotify_specific_code":"absent in apple"
  },
  "amazon":{
    "amzncode":"SPOT",
    "music_quality":"good",
    "stanley":"absent in apple"
  }
}

数据模型为applmusic,spotify and amazon是不同的。但是,我只需要提取applmusic并省略其他编码键。

My Swift数据模型如下:

public struct Applmusic: Codable {
    public let code: String
    public let quality: String
    public let line: String
}

API 使用完整的 JSON 进行响应,我不能要求它只提供所需的字段。

如何只解码json的特定部分?看起来Decodable要求我首先反序列化整个 json,所以我必须知道它的完整数据模型。

显然,解决方案之一是创建一个单独的Response模型只是为了包含applmusic参数,但它看起来像一个黑客:

public struct Response: Codable {
    public struct Applmusic: Codable {
        public let code: String
        public let quality: String
        public let line: String
    }
    // The only parameter is `applmusic`, ignoring the other parts - works fine
    public let applmusic: Applmusic
}

您能否提出一种更好的方法来处理此类 JSON 结构?

多一点洞察力

我在通用扩展中使用以下技术,自动为我解码 API 响应。因此,我更愿意概括一种处理此类情况的方法,而不需要创建一个Root结构。如果我需要的密钥是 JSON 结构中的 3 层怎么办?

这是为我进行解码的扩展:

extension Endpoint where Response: Swift.Decodable {
  convenience init(method: Method = .get,
                   path: Path,
                   codingKey: String? = nil,
                   parameters: Parameters? = nil) {
    self.init(method: method, path: path, parameters: parameters, codingKey: codingKey) {
      if let key = codingKey {
        guard let value = try decoder.decode([String:Response].self, from: $0)[key] else {
          throw RestClientError.valueNotFound(codingKey: key)
        }
        return value
      }

      return try decoder.decode(Response.self, from: $0)
    }
  }
}

API 定义如下:

extension API {
  static func getMusic() -> Endpoint<[Applmusic]> {
    return Endpoint(method: .get,
                    path: "/api/music",
                    codingKey: "applmusic")
  }
}

更新:我做了扩展JSONDecoder从这个答案中,您可以在这里查看:https://github.com/aunnnn/NestedDecodable https://github.com/aunnnn/NestedDecodable,它允许您使用关键路径解码任意深度的嵌套模型。

你可以这样使用它:

let post = try decoder.decode(Post.self, from: data, keyPath: "nested.post")

你可以做一个Decodable包装器(例如,ModelResponse此处),并放置所有逻辑来提取嵌套模型,其中包含一个键:

struct DecodingHelper {

    /// Dynamic key
    private struct Key: CodingKey {
        let stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
            self.intValue = nil
        }

        let intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }

    /// Dummy model that handles model extracting logic from a key
    private struct ModelResponse<NestedModel: Decodable>: Decodable {
        let nested: NestedModel

        public init(from decoder: Decoder) throws {
            let key = Key(stringValue: decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!]! as! String)!
            let values = try decoder.container(keyedBy: Key.self)
            nested = try values.decode(NestedModel.self, forKey: key)
        }
    }

    static func decode<T: Decodable>(modelType: T.Type, fromKey key: String) throws -> T {
        // mock data, replace with network response
        let path = Bundle.main.path(forResource: "test", ofType: "json")!
        let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)

        let decoder = JSONDecoder()

        // ***Pass in our key through `userInfo`
        decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!] = key
        let model = try decoder.decode(ModelResponse<T>.self, from: data).nested
        return model
    }
}

您可以将您想要的密钥传递给userInfo of JSONDecoder ("my_model_key")。然后将其转换为我们的动态Key inside ModelResponse实际提取模型。

然后你可以像这样使用它:

let appl = try DecodingHelper.decode(modelType: Applmusic.self, fromKey: "applmusic")
let amazon = try DecodingHelper.decode(modelType: Amazon.self, fromKey: "amazon")
let spotify = try DecodingHelper.decode(modelType: Spotify.self, fromKey: "spotify")
print(appl, amazon, spotify)

完整代码:https://gist.github.com/aunnnn/2d6bb20b9dfab41189a2411247d04904 https://gist.github.com/aunnnn/2d6bb20b9dfab41189a2411247d04904


奖励:深度嵌套的密钥

经过更多尝试后,我发现您可以使用此修改后轻松解码任意深度的密钥ModelResponse:

private struct ModelResponse<NestedModel: Decodable>: Decodable {
    let nested: NestedModel

    public init(from decoder: Decoder) throws {
        // Split nested paths with '.'
        var keyPaths = (decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!]! as! String).split(separator: ".")

        // Get last key to extract in the end
        let lastKey = String(keyPaths.popLast()!)

        // Loop getting container until reach final one
        var targetContainer = try decoder.container(keyedBy: Key.self)
        for k in keyPaths {
            let key = Key(stringValue: String(k))!
            targetContainer = try targetContainer.nestedContainer(keyedBy: Key.self, forKey: key)
        }
        nested = try targetContainer.decode(NestedModel.self, forKey: Key(stringValue: lastKey)!)
    }

然后你可以像这样使用它:

let deeplyNestedModel = try DecodingHelper.decode(modelType: Amazon.self, fromKey: "nest1.nest2.nest3")

从这个json:

{
    "apple": { ... },
    "amazon": {
        "amzncode": "SPOT",
        "music_quality": "good",
        "stanley": "absent in apple"
    },
    "nest1": {
        "nest2": {
            "amzncode": "Nest works",
            "music_quality": "Great",
            "stanley": "Oh yes",

            "nest3": {
                "amzncode": "Nest works, again!!!",
                "music_quality": "Great",
                "stanley": "Oh yes"
            }
        }
    }
}

完整代码:https://gist.github.com/aunnnn/9a6b4608ae49fe1594dbcabd9e607834 https://gist.github.com/aunnnn/9a6b4608ae49fe1594dbcabd9e607834

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift:可编码 - 提取单个编码密钥 的相关文章

随机推荐

  • 我什么时候应该使用新的 ranged-for 以及我可以将它与新的 cbegin/cend 结合使用吗?

    The new 范围为当然 在 C 11 中会非常简洁和有用 据我了解它是如何工作的 它会查找 容器 begin and end通过尝试 Argument Depending Lookup ADT 但另一个补充是所有容器现在有cbegin
  • 使共享库可供多个应用程序使用的最佳方法是什么?

    与大多数商店一样 我们有一个团队负责各种项目 他们都需要访问与我们业务相关的相同核心信息和功能 通常使用 C 语言 目前 我们只是将公共类从一个项目复制到另一个项目 但每个人都开始有自己的风格 我们希望进行整合 我们使用 Tortoise
  • 如何在 f 字符串表达式中将数字格式化为两位小数?

    我正在尝试制作这个程序 询问用户他们的成绩并以两位小数显示他们的成绩 前任 如果他们的作业得分为 10 15 则会显示作业 1 66 66 在我的代码中 我似乎无法将计算出的数字转换为两位小数 print Please enter your
  • Django:使用 post_delete 信号时确定正在删除的用户

    我希望在删除某些对象时通知管理员 但我也想确定哪个用户正在执行删除 是否可以 这是代码 models py signal to notify admins when nodes are deleted from django db mode
  • 如何锁定 ASP.NET MVC 操作?

    我已经编写了一个用作服务的控制器和操作 这项服务的运行成本相当高 如果当前已存在正在运行的操作 我想限制对此操作的访问 有没有内置的方法来锁定 ASP NET MVC 操作 Thanks 您在寻找这样的东西吗 public MyContro
  • 如何使用准备好的 PDO 语句设置 ORDER BY 参数?

    我在使用参数时遇到问题ORDER BY我的 SQL 部分 它不发出任何警告 但不打印任何内容 order columnName direction ASC stmt db gt prepare SELECT field from table
  • Pandas 在由列表组成的元素上删除重复项

    假设我的数据框是 df pandas DataFrame 1 0 0 0 1 0 产生 0 0 1 0 1 0 0 2 1 0 我想删除重复项 并且只获取元素 1 0 和 0 0 如果我写 df drop duplicates 我收到以下错
  • Python向数据框添加列会导致NaN

    我有一个系列和 df s pd Series 1 2 3 5 df pd DataFrame 当我像这样向 df 添加列时 df loc 0 2 s iloc 0 3 df loc 1 3 s iloc 1 4 I get df 0 2 1
  • 如果存在则更改表,如果不存在则创建

    我需要运行一个安装程序 它也可以是更新程序 安装程序需要能够最终获得 mysql 数据库的特定方案 结构 无论某些表是否存在 丢失了几列 或者因为其结构是最新的而不需要更改 我怎样才能优雅地组合ALTER and CREATE 我在想一定有
  • 在数组中的两对之间添加键/值对

    我一直在寻找和思考 但无法想出一个可行的解决方案来解决这个问题 我有一个带有连续数字键的数组 Example Array 0 gt value 0 1 gt value 1 2 gt value 2 3 gt value 3 我需要在数组中
  • 查询列出数据库中每个表的记录数

    如何列出数据库中每个表的行数 一些相当于 select count from table1 select count from table2 select count from tableN 我将发布一个解决方案 但欢迎其他方法 如果您使用
  • 将对象序列化为 XML 时如何添加 XML 命名空间 (xmlns)

    我正在 XStream 的帮助下将对象序列化为 XML 如何告诉 XStream 将 xmlns 插入到对象的 XML 输出中 例如 我有一个想要序列化的简单对象 XStreamAlias value domain public class
  • 如何在 Spark Pipeline 中使用随机森林

    我想通过网格搜索和 Spark 交叉验证来调整我的模型 在 Spark 中 它必须将基础模型放入管道中 即管道办公室演示 http spark apache org docs latest ml guide html example mod
  • ASP.NET 自定义错误被忽略

    我在 web config 中配置了自定义错误 但 IIS 6 0 返回网站配置的 自定义错误 选项卡中指定的自定义错误
  • Coverage.py 无法发现子目录中没有 init.py 文件的测试

    当我运行 python 覆盖范围时 我总是需要一个空的 init py测试子目录中的文件以获得运行测试的覆盖范围 这是 python2 软件包的要求 但不是 python3 的要求 为了重现 我执行了以下操作 先决条件是 python3 p
  • XPath 和 Regexp 哪个更快?

    我正在为 firefox 制作一个附加组件 它使用 ajax 加载一个 html 页面 附加组件有它的 XUL 面板 现在 我没有寻找创建document对象并将ajax请求内容放入其中 然后使用xPath找到我需要的内容 相反 我加载内容
  • 用于云服务“沙盒”的 SecurityManager

    All 我正在设计一个基于云的服务 该服务将提供执行客户提交的一些 插件 代码的选项 为了使这项工作正常进行 插件不能威胁系统完整性或有能力访问其他客户端的数据 这一点至关重要 理想情况下 我希望客户端能够提交一个简单的 jar 文件 包含
  • 我应该为 Windows 选择 ActivePerl 还是 Strawberry Perl? [复制]

    这个问题在这里已经有答案了 我对 Perl 完全陌生 但我想尝试一下 我读到了有关 Windows 平台的两个竞争对手的发行版 我猜有just其他操作系统上的 Perl 维基百科 http en wikipedia org wiki Per
  • .NET Core 锁定文件

    我有一个 ASP NET Core 应用程序 我通过运行命令来运行应用程序 dotnet run 当我构建此 ASP NET Core 应用程序时 我在五分之一的情况下会看到以下错误 C error CS2012 无法打开 C bin De
  • Swift:可编码 - 提取单个编码密钥

    我有以下代码来提取编码密钥中包含的 JSON let value try decoder decode String Applmusic self from 0 applmusic 这成功处理了以下 JSON applmusic code