KeyedDecodingContainerProtocol.superDecoder(forKey:) 不按照文档所说的工作。

huangapple go评论70阅读模式
英文:

KeyedDecodingContainerProtocol.superDecoder(forKey:) doesn't work as documentation says

问题

以下是您要翻译的内容:

我有以下由后端返回的 JSON:

```json
[
   {
      "id":8,
      "title":"Title 1",
      "componentCategoryIcon":{
         "id":34,
         "name":"icon_name_1",
         "type":"IMAGE"
      }
   },
   {
      "id":8,
      "title":"Title 2",
      "componentCategoryIcon":{
         "id":35,
         "name":"icon_name_2",
         "type":"IMAGE"
      }
   },
   {
      "id":8,
      "title":"Title 3",
      "componentCategoryIcon": null
   }
]

以及以下的 Codable 结构体:

struct ComponentCategory: Codable {
    let id: Int
    let title: String
    let componentCategoryIcon: Image?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let id = try container.decode(Int.self, forKey: .id)
        
        self.id = id
        self.title = try container.decode(String.self, forKey: .title)
        
        let savedComponentCategory = (decoder.userInfo[.savedComponentCategories] as? [ComponentCategory])?.first(where: { $0.id == id })
        

        // (1)
        if let nestedDecoder = try? container.superDecoder(forKey: .componentCategoryIcon) {
            self.componentCategoryIcon = try Image(from: nestedDecoder, savedImage: savedComponentCategory?.componentCategoryIcon)
        } else {
            self.componentCategoryIcon = nil
        }
    }
}

struct Image: Codable {
    let id: Int
    let name: String
    let type: String?
    let path: String?
    
    let downloaded: Bool
    
    required init(from decoder: Decoder, savedImage: Image?) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.type = try container.decodeIfPresent(String.self, forKey: .type)
        self.path = try container.decodeIfPresent(String.self, forKey: .path)
        self.downloaded = try container.decodeIfPresent(Bool.self, forKey: .downloaded) ?? savedImage?.downloaded ?? false
    }
}

现在,我注意到了与 superDecoder(forKey:) 函数文档不同的行为。文档中说:

抛出

如果给定键的值为 null,则抛出 DecodingError.valueNotFound

在第三次解码 ComponentCategory 时(componentCategoryIconnull),我预期会进入(1)if-else 的 else 分支。但实际上,我得到了一个似乎有效的 nestedContainer 并且在 Image 的初始化中出现了异常。

我是否误解了 superDecoder(forKey:) 文档?还是文档有错误?

英文:

I have the following JSON returned by my backend:

[
   {
      "id":8,
      "title":"Title 1",
      "componentCategoryIcon":{
         "id":34,
         "name":"icon_name_1",
         "type":"IMAGE"
      }
   },
   {
      "id":8,
      "title":"Title 2",
      "componentCategoryIcon":{
         "id":35,
         "name":"icon_name_2",
         "type":"IMAGE"
      }
   },
   {
      "id":8,
      "title":"Title 3",
      "componentCategoryIcon": null
   }
]

And the following Codable structs:

struct ComponentCategory: Codable {
    let id: Int
    let title: String
    let componentCategoryIcon: Image?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let id = try container.decode(Int.self, forKey: .id)
        
        self.id = id
        self.title = try container.decode(String.self, forKey: .title)
        
        let savedComponentCategory = (decoder.userInfo[.savedComponentCategories] as? [ComponentCategory])?.first(where: { $0.id == id })
        

        // (1)
        if let nestedDecoder = try? container.superDecoder(forKey: .componentCategoryIcon) {
            self.componentCategoryIcon = try Image(from: nestedDecoder, savedImage: savedComponentCategory?.componentCategoryIcon)
        } else {
            self.componentCategoryIcon = nil
        }
    }
}
struct Image: Codable {
    let id: Int
    let name: String
    let type: String?
    let path: String?
    
    let downloaded: Bool
    
    required init(from decoder: Decoder, savedImage: Image?) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.type = try container.decodeIfPresent(String.self, forKey: .type)
        self.path = try container.decodeIfPresent(String.self, forKey: .path)
        self.downloaded = try container.decodeIfPresent(Bool.self, forKey: .downloaded) ?? savedImage?.downloaded ?? false
    }

Now I noted a behaviour different from documentation for superDecoder(forKey:) func. Documentation says:

> Throws
>
> DecodingError.valueNotFound if self has a null entry for the given key.

During third ComponentCategory decoding (componentCategoryIcon is null), I expected to go to else branch of the (1) if-else. Instead I obtain an apparently valid nestedContainer and I have an exception in the Image init.

Am I misunderstanding superDecoder(forKey:) documentation? Or is it wrong?

答案1

得分: 1

The current implementation确实不会在没有给定键的情况下引发错误,而是返回一个空容器;文档与此实现不匹配。似乎即将推出的swift-foundation包也保持了这种行为,所以文档需要更新。

这很可能值得提出一个问题(无论是用于修复错误还是修复文档)。

与此同时,您可以通过检查所需键的显式存在来解决此问题,并将其用作if语句的条件。


尽管如上面的评论所述,您尝试使用superDecoder似乎令人惊讶。您可以考虑要么在您的问题中添加有关为什么需要这样做的信息,要么提出一个新问题,询问您可能希望采取的其他方法建议。

英文:

The current implementation indeed does not throw an error if there is no value for the given key, and instead returns an empty container; the documentation does not match this implementation. It appears that the upcoming swift-foundation package also maintains this behavior, so the documentation stands to get updated.

This is likely worth filing an issue for (whether for a bug fix, or a documentation fix).

In the meantime, you can work around this by checking for the explicit existence of the key you need, and use that as the condition for your if-statement.


This being said, like the comments above mention, it seems surprising you would try to use a superDecoder for this. You may want to either add information to your question about why you need to do this, or open a new question asking for suggestions on what approach you might want to take instead.

huangapple
  • 本文由 发表于 2023年6月6日 00:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76408205.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定