使用enum时从JSON响应中删除前导零



本质上,我使用enum来说明code是int或string的实例-但当JSON值只是一个int时,就会出现像0039223这样的前置0's,它们会被修剪掉。如何预防这种情况?

试着在操场上运行这段代码:

let data = """
[
{
"date": "2022-05-04",
"code": 0039223,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]
""".data(using: .utf8)!
enum Code: Decodable {
case int(Int)
case string(String)
}
struct Item: Decodable {
var date: Date
var code: Code
var notes: String
enum CodingKeys: String, CodingKey {
case date, code, notes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.date = try container.decode(Date.self, forKey: .date)
self.notes = try container.decode(String.self, forKey: .notes)
if let value = try? container.decode(Int.self, forKey: .code) {
self.code = .int(value)
} else if let value = try? container.decode(String.self, forKey: .code) {
self.code = .string(value)
} else {
let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unable to decode value for `code`")
throw DecodingError.typeMismatch(Code.self, context)
}
}
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let items = try! decoder.decode([Item].self, from: data)
for item in items {
print("date: (item.date.description)")
print("code: (item.code)")
print("notes: (item.notes)")
print()
}

JSON无效。理论上,您可以清理它,删除"code"出现的数字中的前导零。例如,转换为字符串,修剪前导零(例如,使用正则表达式),然后转换回Data:

guard
let string = String(data: data, encoding: .utf8)?
.replacingOccurrences(of: #""code"s*:s*0+(?!b)"#, with: #""code": "#, options: .regularExpression),
let fixedData = string.data(using: .utf8)
else { return }

转换无效的JSON:

[
{
"date": "2022-05-04",
"code": 0039223,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]

为:

[
{
"date": "2022-05-04",
"code": 39223,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]

然后可以解码fixedData


或者,您可以使code成为String,消除所有自定义解码器代码:

struct Item: Decodable {
var date: Date
var code: String
var notes: String
}

然后让正则表达式引用未引用的整数,使它们可解析为字符串:

guard
let string = String(data: data, encoding: .utf8)?
.replacingOccurrences(of: #""code"s*:s*(d+)"#, with: #""code": "$1""#, options: .regularExpression),
let data2 = string.data(using: .utf8)
else { return }

将:

[
{
"date": "2022-05-04",
"code": 0039223,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]

[
{
"date": "2022-05-04",
"code": "0039223",
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]

将服务器响应简化为不需要自定义解码器的内容。


但这两种方法都是低效的,脆弱的,有点坚果。JSON是不正确的,应该在源端修复,而不是客户端。

因此,基于检查json值是否匹配带前导零的数字并将其转换为字符串和rfc4267, json不支持带前导零的整数。

那么,你怎么解决它呢?正确的答案是在源端修复它,生成JSON的任何东西都应该更新以满足格式的标准要求,但是,我花了很多时间与后端开发人员打交道,这可能"太难了"。(如果你觉得"太难"的话)对生产商来说,为什么你会认为消费者会更容易呢?🤨)

所以,松散地基于转换字符串?,你唯一的选择就是"修复"。json文本如何

那么,如果我们这样做…

"""
[
{
"date": "2022-05-04",
"code": 0039223,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]
"""
.replacingOccurrences(of: "00\d+", with: ""$0"", options: .regularExpression)

我们最终会…

[
{
"date": "2022-05-04",
"code": "0039223",
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]

我只是想在这里指出一些事情,这是非常幼稚的,因为任何以00开头的文本都会被捕获,而且,因为你有带前导零的日期值,它会变得有问题,因为你最终可能会"修复"。

这就是为什么我说,从源头上解决问题是解决这个问题的正确方法,因为你在你的一端做的任何事情都只会给你带来无休止的头痛,因为尝试和补偿每一个可能的边缘情况

哦,这就是我解析它的方式,所以你仍然可以有.int.string代码

struct Item: Decodable {
var date: Date
var code: Code
var notes: String
enum CodingKeys: String, CodingKey {
case date, code, notes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.date = try container.decode(Date.self, forKey: .date)
self.notes = try container.decode(String.self, forKey: .notes)

if let value = try? container.decode(String.self, forKey: .code) {
if let intValue = Int(value) {
self.code = .int(intValue)
} else {
self.code = .string(value)
}
} else {
let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unable to decode value for `code`")
throw DecodingError.typeMismatch(Code.self, context)
}
}
}

相关内容

  • 没有找到相关文章

最新更新