Swift 4 Decodable - Dictionary with enum as key

前端 未结 3 2106
我在风中等你
我在风中等你 2020-12-01 06:27

My data structure has an enum as a key, I would expect the below to decode automatically. Is this a bug or some configuration issue?

import Foundation

enum          


        
3条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-01 06:48

    In order to solve your problem, you can use one of the two following Playground code snippets.


    #1. Using Decodable's init(from:) initializer

    import Foundation
    
    enum AnEnum: String, Codable {
        case enumValue
    }
    
    struct AStruct {
        enum CodingKeys: String, CodingKey {
            case dictionary
        }
        enum EnumKeys: String, CodingKey {
            case enumValue
        }
    
        let dictionary: [AnEnum: String]
    }
    
    extension AStruct: Decodable {
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let dictContainer = try container.nestedContainer(keyedBy: EnumKeys.self, forKey: .dictionary)
    
            var dictionary = [AnEnum: String]()
            for enumKey in dictContainer.allKeys {
                guard let anEnum = AnEnum(rawValue: enumKey.rawValue) else {
                    let context = DecodingError.Context(codingPath: [], debugDescription: "Could not parse json key to an AnEnum object")
                    throw DecodingError.dataCorrupted(context)
                }
                let value = try dictContainer.decode(String.self, forKey: enumKey)
                dictionary[anEnum] = value
            }
            self.dictionary = dictionary
        }
    
    }
    

    Usage:

    let jsonString = """
    {
      "dictionary" : {
        "enumValue" : "someString"
      }
    }
    """
    
    let data = jsonString.data(using: String.Encoding.utf8)!
    let decoder = JSONDecoder()
    let aStruct = try! decoder.decode(AStruct.self, from: data)
    dump(aStruct)
    
    /*
     prints:
     ▿ __lldb_expr_148.AStruct
       ▿ dictionary: 1 key/value pair
         ▿ (2 elements)
           - key: __lldb_expr_148.AnEnum.enumValue
           - value: "someString"
     */
    

    #2. Using KeyedDecodingContainerProtocol's decode(_:forKey:) method

    import Foundation
    
    public enum AnEnum: String, Codable {
        case enumValue
    }
    
    struct AStruct: Decodable {
        enum CodingKeys: String, CodingKey {
            case dictionary
        }
    
        let dictionary: [AnEnum: String]
    }
    
    public extension KeyedDecodingContainer  {
    
        public func decode(_ type: [AnEnum: String].Type, forKey key: Key) throws -> [AnEnum: String] {
            let stringDictionary = try self.decode([String: String].self, forKey: key)
            var dictionary = [AnEnum: String]()
    
            for (key, value) in stringDictionary {
                guard let anEnum = AnEnum(rawValue: key) else {
                    let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Could not parse json key to an AnEnum object")
                    throw DecodingError.dataCorrupted(context)
                }
                dictionary[anEnum] = value
            }
    
            return dictionary
        }
    
    }
    

    Usage:

    let jsonString = """
    {
      "dictionary" : {
        "enumValue" : "someString"
      }
    }
    """
    
    let data = jsonString.data(using: String.Encoding.utf8)!
    let decoder = JSONDecoder()
    let aStruct = try! decoder.decode(AStruct.self, from: data)
    dump(aStruct)
    
    /*
     prints:
     ▿ __lldb_expr_148.AStruct
       ▿ dictionary: 1 key/value pair
         ▿ (2 elements)
           - key: __lldb_expr_148.AnEnum.enumValue
           - value: "someString"
     */
    

提交回复
热议问题