Swift Codable:如何将顶级数据编码到嵌套容器中

2024-02-26

我的应用程序使用返回 JSON 的服务器,如下所示:

{
    "result":"OK",
    "data":{

        // Common to all URLs
        "user": {
            "name":"John Smith" // ETC...
        },

        // Different for each URL
        "data_for_this_url":0
    }
}

正如您所看到的,特定于 URL 的信息与常见的信息存在于同一字典中。user字典。

GOAL:

  1. Decode this JSON into classes/structs.
    • Because user很常见,我希望它位于顶级类/结构中。
  2. Encode to new format (e.g. plist).
    • 我需要保留原来的结构。 (即重新创建data来自顶级的字典user信息和子对象的信息)

PROBLEM:

重新编码数据时,我不能同时写入user字典(来自顶级对象)和特定于 URL 的数据(来自子对象)到编码器。

Either user覆盖其他数据,或者其他数据覆盖user。我不知道如何将它们结合起来。

这是我到目前为止所拥有的:

// MARK: - Common User
struct User: Codable {
    var name: String?
}

// MARK: - Abstract Response
struct ApiResponse<DataType: Codable>: Codable {
    // MARK: Properties
    var result: String
    var user: User?
    var data: DataType?

    // MARK: Coding Keys
    enum CodingKeys: String, CodingKey {
        case result, data
    }
    enum DataDictKeys: String, CodingKey {
        case user
    }

    // MARK: Decodable
    init(from decoder: Decoder) throws {
        let baseContainer = try decoder.container(keyedBy: CodingKeys.self)
        self.result = try baseContainer.decode(String.self, forKey: .result)
        self.data = try baseContainer.decodeIfPresent(DataType.self, forKey: .data)

        let dataContainer = try baseContainer.nestedContainer(keyedBy: DataDictKeys.self, forKey: .data)
        self.user = try dataContainer.decodeIfPresent(User.self, forKey: .user)
    }

    // MARK: Encodable
    func encode(to encoder: Encoder) throws {
        var baseContainer = encoder.container(keyedBy: CodingKeys.self)
        try baseContainer.encode(self.result, forKey: .result)

        // MARK: - PROBLEM!!

        // This is overwritten
        try baseContainer.encodeIfPresent(self.data, forKey: .data)

        // This overwrites the previous statement
        var dataContainer = baseContainer.nestedContainer(keyedBy: DataDictKeys.self, forKey: .data)
        try dataContainer.encodeIfPresent(self.user, forKey: .user)
    }
}

EXAMPLE:

在下面的示例中,重新编码的 plist 不包括order_count,因为它被包含的字典覆盖了user.

// MARK: - Concrete Response
typealias OrderDataResponse = ApiResponse<OrderData>

struct OrderData: Codable {
    var orderCount: Int = 0
    enum CodingKeys: String, CodingKey {
        case orderCount = "order_count"
    }
}


let orderDataResponseJson = """
{
    "result":"OK",
    "data":{
        "user":{
            "name":"John"
        },
        "order_count":10
    }
}
"""

// MARK: - Decode from JSON
let jsonData = orderDataResponseJson.data(using: .utf8)!
let response = try JSONDecoder().decode(OrderDataResponse.self, from: jsonData)

// MARK: - Encode to PropertyList
let plistEncoder = PropertyListEncoder()
plistEncoder.outputFormat = .xml

let plistData = try plistEncoder.encode(response)
let plistString = String(data: plistData, encoding: .utf8)!

print(plistString)

// 'order_count' is not included in 'data'!

/*
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>data</key>
    <dict>
        <key>user</key>
        <dict>
            <key>name</key>
            <string>John</string>
        </dict>
    </dict>
    <key>result</key>
    <string>OK</string>
</dict>
</plist>
*/

我刚刚在查看编码器协议时顿悟了。

KeyedEncodingContainerProtocol.superEncoder(forKey:)方法正是针对这种情况。

该方法返回一个单独的Encoder可以收集多个项目和/或嵌套容器,然后将它们编码为单个密钥。

对于这个具体案例,顶层user数据可以通过简单地调用自己的编码来编码encode(to:)方法,用新的superEncoder。然后,还可以使用编码器创建嵌套容器,并正常使用。

问题的解答

// MARK: - Encodable
func encode(to encoder: Encoder) throws {

    var baseContainer = encoder.container(keyedBy: CodingKeys.self)
    try baseContainer.encode(self.result, forKey: .result)

    // MARK: - PROBLEM!!
//    // This is overwritten
//    try baseContainer.encodeIfPresent(self.data, forKey: .data)
//
//    // This overwrites the previous statement
//    var dataContainer = baseContainer.nestedContainer(keyedBy: DataDictKeys.self, forKey: .data)
//    try dataContainer.encodeIfPresent(self.user, forKey: .user)

    // MARK: - Solution
    // Create a new Encoder instance to combine data from separate sources.
    let dataEncoder = baseContainer.superEncoder(forKey: .data)

    // Use the Encoder directly:
    try self.data?.encode(to: dataEncoder)

    // Create containers for manually encoding, as usual:
    var userContainer = dataEncoder.container(keyedBy: DataDictKeys.self)
    try userContainer.encodeIfPresent(self.user, forKey: .user)
}

Output:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>data</key>
    <dict>
        <key>order_count</key>
        <integer>10</integer>
        <key>user</key>
        <dict>
            <key>name</key>
            <string>John</string>
        </dict>
    </dict>
    <key>result</key>
    <string>OK</string>
</dict>
</plist>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift Codable:如何将顶级数据编码到嵌套容器中 的相关文章

  • NSString stringWithContentsOfFile 失败,错误代码似乎错误

    我正在尝试将文件加载到字符串中 这是我正在使用的代码 NSError error nil NSString fullPath NSBundle mainBundle pathForResource filename ofType html
  • .showsPhysics 内存泄漏

    我最近花了 5 个小时尝试调试 Spritekit 应用程序中的内存泄漏 应用程序启动后 我注意到内存使用量略有上升 我花了 5 个小时中的 3 个小时挖掘参考资料 了解强与弱的关系ARC https developer apple com
  • 如何从 ContentView 外部显示 SwiftUI 警报?

    我正在构建 Swift 应用程序 并试图找出如何显示警报 我有一个单独的 swift 文件正在执行一些计算 并且在某些条件下我希望它向用户显示警报 基本上告诉他们出了问题 然而 我见过的大多数例子都要求警报在ContentView或以其他方
  • iOS、通用链接、Swift。 continueUserActivity 未调用

    我正在为我们的 iOS 应用程序实现通用链接 这是我的一小部分 AppDelegate private func application application UIApplication openURL url URL sourceApp
  • AppStore 提交:错误 ITMS-9000:“无效的捆绑结构 - 不允许二进制文件‘MyApp.app/BuildAgent’

    我陷入了以下错误 我根本不明白 错误 ITMS 9000 无效的捆绑结构 不允许使用二进制文件 MyApp app BuildAgent 您的应用程序可能只包含一个可执行文件 当我使用 Xcode 从 Archive 导出到 IPA 时 我
  • locationOfTouch 和 numberOfTouches

    你好 我有这个识别器 设置为 2 次触摸 但它只返回一个 而不是两个 CGPoint void gestureLoad UIGestureRecognizer recognizer recognizer UITapGestureRecogn
  • 错误域=kAFAssistantErrorDomain 代码=209“(空)”

    我面临着一个问题SFSpeechRecognizer 启动应用程序几秒钟后 我开始收到错误消息 错误域 kAFAssistantErrorDomain 代码 209 空 和 错误 域 kAFAssistantErrorDomain 代码 2
  • 有关 Swift 编译器选项的文档

    您好 我想开始在 Apple Swift 语言上运行一些微基准测试 然而 我觉得很难找到有关编译器优化的不同选项的适当文档 我读过很多关于其他人的语言微基准的问题和文章 但是如果能有一些关于该主题的可靠文档那就太好了 在最新的测试版中 使用
  • 每 24 小时触发一次方法

    我正在尝试每天在给定时间触发一个方法 我尝试了一些方法 但我无法真正使其发挥作用 任何意见 将不胜感激 此外 如果无论应用程序是否打开它都会触发 那就更理想了 这可能吗 UI本地通知 http developer apple com lib
  • 如何在 Swift 中从 UIColor 获取 RGB 代码(INT)[重复]

    这个问题在这里已经有答案了 我想在 Swift 中获取 UIColor 的 RGB 值 let swiftColor UIColor red 1 green 165 255 blue 0 alpha 1 println RGB Value
  • 使用未解析的标识符“FlurryAdInterstitial”

    我正在尝试整合Flurry Interstitial Ads使用cocoapods in Swift and Xcode 7 1 1 我正在关注开发人员雅虎网站上的此文档 https developer yahoo com flurry d
  • UIViewControllerAnimatedTransitioning:旋转更改后黑屏片段

    我已经创建了一个视图控制器转换 只要我不更改设备方向 一切都正常 图 1 显示了应有的屏幕 然后我切换到下一个视图控制器 在其中更改方向 现在我回到第一个视图控制器并再次切换方向 然后我得到的结果如图 2 所示 出现黑色边框 请不要介意屏幕
  • Swift - 元类型 .Type 和 .self 之间有什么区别?

    元类型有什么区别 Type and self在斯威夫特 Do self and Type返回一个struct 我明白那个 self可以用来检查dynamicType 你如何使用 Type 首先也是最重要的是查看 Apple 文档type o
  • 调整 UIImage 的大小而不将其完全加载到内存中?

    我正在开发一个应用程序 用户可以在其中尝试加载非常非常大的图像 这些图像首先在表格视图中显示为缩略图 我的原始代码会在大图像上崩溃 因此我重写它以首先将图像直接下载到磁盘 是否有一种已知的方法可以调整磁盘上图像的大小 而无需通过以下方式将其
  • “预期的 ';'在 Swift 下的顶级声明符之后”

    我正在尝试将所有颜色设置在一个 Swift 文件中 该文件可以在我的整个应用程序中使用 下面的代码会导致 import Foundation import UIKit class DotColors let tsblueColor UICo
  • 如何在button.addTarget操作中发送多个按钮?斯威夫特3

    如何将button和button2发送到我的pressButton2函数中 当用户触摸按钮2时 我需要更改按钮和按钮2的颜色 当我的 button2 addTarget 看起来像这样时 我收到错误 表达式列表中存在预期表达式 import
  • UItextView 背景颜色 Linespacing 区域太

    我正在尝试在 UITextView 中复制文本突出显示 不是搜索文本突出显示 但我也被行间距的颜色所困扰 我该如何纠正这个问题 现在的情况 期望的结果 我已将以下属性添加到我的 UiTextview 的属性文本中 对于段落行间距 我使用了以
  • 叠加 SKScene 未显示

    我正在尝试将 SKScene 覆盖在 SCNScene 上 当我在模拟器和 iPhone6 上运行我的应用程序时 overlayScene SKScene 按预期显示 但是当我尝试在 iPhone5 上运行它 尝试了 2 个不同的设备 时
  • iPhone 上的纵向 UISplitViewController 在 iOS 8 中始终显示主视图和细节视图

    UISplitViewController in portrait在 iPhone 上始终显示主控和细节iOS 8 我尝试子类化UISplitViewController并将其配置为同时显示主视图和细节视图 但没有任何效果 class AP
  • SpriteKit的更新函数:时间与帧率

    一般来说 我对编程和 Spritekit 很陌生 并且有兴趣探索毫秒和帧率之间的关系 以及如何使用更新函数作为两者之间的中介 帧率与毫秒 从本质上讲 帧速率和时间之间的主要区别在于时间始终一致 而帧速率则不然 由于密集的图形程序 它可能会下

随机推荐

  • WPF:如何冻结数据网格中的列标题

    如何将我的列标题冻结在DataGrid in my WPF窗口 以便当我向下滚动时 标题仍然可见 Edit 这是我的XAML
  • Scrapy 将子站点项目与站点项目合并

    我试图从子网站中抓取详细信息并与网站中抓取的详细信息合并 我一直在通过 stackoverflow 以及文档进行研究 但是 我仍然无法让我的代码工作 看来我从子网站提取附加详细信息的功能不起作用 如果有人能看一下我将非常感激 coding
  • 测试元素的类型 python tuple/list

    如何验证列表或元组中所有元素的类型是否相同并且属于某种类型 例如 1 2 3 test for all int True 1 3 a test for all int False all isinstance n int for n in
  • 日期输入的 onchange [重复]

    这个问题在这里已经有答案了 可能的重复 当 的值发生更改时 会触发哪些事件 https stackoverflow com questions 3940258 what events does an input type number fi
  • 从 grails 项目执行甘特脚本

    我已经编写了自己的甘特脚本 它可以在命令行中正常工作 现在我需要从 grails 项目运行这个脚本 如下所示 def outputMessage try GroovyScriptEngine engine new GroovyScriptE
  • 如何使用 TFS REST API 获取迭代剩余天数

    我目前正在使用REST API version 2 0并连接到我的 TFS 实例PowerShell 我可以得到以下信息 迭代ID迭代名称队员团队成员每天的容量 使用下面的示例 GET https instance DefaultColle
  • 在 C# 中扩展枚举

    在java中 我习惯于扩展枚举值或重写方法 如下所示 enum SomeEnum option1 sv public String toString return Some value option2 private String Pass
  • Scala:我可以依赖集合中项目的顺序吗?

    这是一个相当不愉快的意外 scala gt Set 1 2 3 4 5 res18 scala collection immutable Set Int Set 4 5 1 2 3 scala gt Set 1 2 3 4 5 toList
  • Firebase 控制台中出现“您的操作被禁止”问题

    我创建了一个 Android 项目 现在我想将 Firebase 添加到我的 Android 项目中 我在 firebase 控制台上注册我的应用程序 现在 当我尝试将 sha 1 密钥添加到项目中时 出现以下错误 我在谷歌和 stacko
  • Reactjs - 必须返回有效的 React 元素(或 null)

    我有以下简单的代码 var data email email protected cdn cgi l email protection email email protected cdn cgi l email protection var
  • 对特定类的通用约束,为什么? [复制]

    这个问题在这里已经有答案了 我一直在阅读有关利用泛型约束的内容 我发现泛型可以被限制为struct class new Class and Interface 前三个背后的原因非常明显 但我实在无法理解why and when约束到一个类
  • javascript中当数组键包含字符串时删除数组键

    我在 javascript 中有一个数组 如下所示 arr md51234 md55234 我试图通过执行以下操作从中删除一个项目 delete arr md51234 但这似乎不起作用 还有其他方法可以删除这个吗 dystroy 提供了答
  • Java 日期和时间 API 有什么问题? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我经常遇到关于 Java 的负面反馈Date以及其他与日期时间相关的课程 作为一名 NET 开发人员 我无法完全 没有使用过它们 理解它们到底出
  • jQuery.Deferred 异常:$(...).datepicker 不是函数

    提前致谢 我已经搜索并实施了 document ready function ui datepicker datepicker and function if Modernizr inputtypes date input type dat
  • 如何在Netty中使用多个ServerBootstrap对象

    我正在尝试使用 Netty 4 0 24 在一个应用程序 一个主要方法 中创建多个服务器 多个 ServerBootstrap 我看到了这个问题 答案 但它留下了许多未解答的问题 Netty 4 0多端口 每个端口有不同的协议 https
  • 如何在ejb 3.0中实现缓存?

    我有一位客户陷入 EJB 3 0 环境中 没有 Singleton 没有bean管理的并发 考虑到ejb规范禁止线程管理和同步 如何实现缓存 本质上 我想要一个非同步对象缓存来执行一些昂贵的操作 EJB 3 0 规范第 21 1 2 章中规
  • 使用 Angular 14 在运行时动态导入模块

    我试图在 Angular 14 中动态导入模块 其中模块路径是在运行时设置的 但出现以下错误 Error Cannot find module src app plugin1 plugin1 module Github 重现 https g
  • 字符指针和整数指针 (++)

    我有两个指点 char str1 int str2 如果我查看两个指针的大小 我们假设 str1 4 bytes str2 4 bytes str1 将增加 1 个字节 但如果 str2 将增加 4 个字节 这背后的理念是什么 很简单 在提
  • System.Drawing.Bitmap 和 System.Windows.Media.Imaging.WriteableBitmap 之间的区别

    2者有什么区别 一些例子会很棒 没有System Drawing Bitmap在银光中 如果您要求在 NET 框架和WritableBitmap在 Silverlight 中 差异是巨大的 这WritableBitmap是位图的简单表示 具
  • Swift Codable:如何将顶级数据编码到嵌套容器中

    我的应用程序使用返回 JSON 的服务器 如下所示 result OK data Common to all URLs user name John Smith ETC Different for each URL data for thi