关于swift:关于JSONDecoder的原始类型的自定义初始化器

Custom intialiser on Primitive types for JSONDecoder

如何为primitive类型(如Int,Bool)自定义JSONDecoder的行为?

这是问题所在:

  • 不能依赖于后端的类型。例如:布尔值可以为true / false或" true" /" false"(布尔值可以用双引号引起来)

  • 我们至少有300个具有平均15个属性的Codable结构,并且编写解码逻辑很麻烦。而且逻辑保持或多或少相同,因此代码变得重复

因此,我正在寻找一种解决方案,如果存在Type mismatch基本类型,则应能够处理它;如果没有,则应将其设置为nil,前提是该类型为Optional。


我为此尝试了多种方法
1.在所有原始类型上都具有包装器,并处理解码逻辑。以下是Bool上的包装器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Bool1: Codable{
    private var bool: Bool?

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let b = try? container.decode(Bool.self) {
            self.bool = b
        } else if let string = try? container.decode(String.self) {
            if string.lowercased() =="true" {
                self.bool = true
            } else if string.lowercased() =="false" {
                self.bool = false
            } else {
                throw Error()
            }
        }
    }
}

但这在其他开发人员之间造成了不必要的混乱,因为包装类型不如原生类型自然出现。同样,不能直接访问该值(始终需要xyz.bool)来提取原始值

2.创建一个从Decodable和子类JSONDecoder继承的新协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protocol KKDecodable: Decodable {
    init(decoder1: Decoder)
}

extension Bool: KKDecodable {
    init(decoder1: Decoder) {
     // Logic for creating Bool from different types
    }
}

class JSONDecoder1: JSONDecoder {
    func decode< T >(_ type: T.Type, from data: Data) throws -> T where T : KKDecodable {
         // Some code that would invoke `init(decoder1: Decoder)`
         // which is defined in `KKDecodable`
    }
}

我无法使用此方法编写工作代码


物业包装

您可以使用属性包装器。 想象一下这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@propertyWrapper
struct SomeKindOfBool: Decodable {
    var wrappedValue: Bool?
   
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let stringifiedValue = try? container.decode(String.self) {
            switch stringifiedValue.lowercased() {
            case"false": wrappedValue = false
            case"true": wrappedValue = true
            default: wrappedValue = nil
            }
        } else {
            wrappedValue = try? container.decode(Bool.self)
        }
    }
}

用法

1
2
3
struct MyType: Decodable {
    @SomeKindOfBool var someKey: Bool?
}

您现在可以像普通的Bool一样使用someKey值:

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
let jsonData ="""
[
 {"someKey":"something else" },
 {"someKey":"true" },
 {"someKey": true }
]
""".data(using: .utf8)!

let decodedJSON = try! JSONDecoder().decode([MyType].self, from: jsonData)

for decodedType in decodedJSON {
    print(decodedType.someKey)
}

结果:

nil

Optional(true)

Optional(true)


您可以针对其他情况以及所需的任何其他类型执行类似的操作。 还要注意,我已经更改了代码以满足您的需求,但是您可以使用我在GitHub上发布的更具兼容性的版本