dr_*_*rto 39 json ios swift4 codable
我正在使用Swift 4 JSONEncoder.我有一个Codable带有可选属性的结构,我希望这个属性在null值为时在生成的JSON数据中显示为值nil.但是,JSONEncoder丢弃该属性并且不将其添加到JSON输出.有没有办法配置,JSONEncoder以便它保留密钥并null在这种情况下将其设置为?
下面的代码片段会产生{"number":1},但我更愿意给它{"string":null,"number":1}:
struct Foo: Codable {
var string: String? = nil
var number: Int = 1
}
let encoder = JSONEncoder()
let data = try! encoder.encode(Foo())
print(String(data: data, encoding: .utf8)!)
Run Code Online (Sandbox Code Playgroud)
Rob*_*ier 34
是的,但你必须编写自己的编码器; 你不能使用默认的.
struct Foo: Codable {
var string: String? = nil
var number: Int = 1
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(number, forKey: .number)
try container.encode(string, forKey: .string)
}
}
Run Code Online (Sandbox Code Playgroud)
直接编码可选项将编码null,就像您正在寻找的那样.
如果这是一个重要的用例,你可以考虑在bugs.swift.org上打开一个缺陷,要求在JSONEncoder上encode(to:)添加一个新的标志以匹配现有的OptionalEncodingStrategy等等.(见下文为什么这可能实际上是不可能的)今天在Swift中实现,但是随着Swift的发展,进入跟踪系统仍然很有用.)
编辑:对于下面的Paulo的问题,这将调度到通用DateEncodingStrategy版本,因为encode<T: Encodable>符合Optional.这是通过这种方式在Codable.swift中实现的:
extension Optional : Encodable /* where Wrapped : Encodable */ {
@_inlineable // FIXME(sil-serialize-all)
public func encode(to encoder: Encoder) throws {
assertTypeIsEncodable(Wrapped.self, in: type(of: self))
var container = encoder.singleValueContainer()
switch self {
case .none: try container.encodeNil()
case .some(let wrapped): try (wrapped as! Encodable).__encode(to: &container)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这包含了调用Encodable,我认为让stdlib处理Optionals只是另一个Encodable比在我们自己的编码器中将它们视为一个特殊情况并调用encodeNil自己更好.
另一个显而易见的问题是为什么它首先以这种方式工作.由于Optional是Encodable,并且生成的Encodable一致性对所有属性进行编码,为什么"手动编码所有属性"的工作方式不同?答案是一致性生成器包含一个特殊情况:Optionals:
// Now need to generate `try container.encode(x, forKey: .x)` for all
// existing properties. Optional properties get `encodeIfPresent`.
...
if (varType->getAnyNominal() == C.getOptionalDecl() ||
varType->getAnyNominal() == C.getImplicitlyUnwrappedOptionalDecl()) {
methodName = C.Id_encodeIfPresent;
}
Run Code Online (Sandbox Code Playgroud)
这意味着改变这种行为需要改变自动生成的一致性,而不是encodeNil(这也意味着它可能很难在今天的Swift中进行配置......)
g-m*_*ark 24
这是一种使用属性包装器的方法(需要 Swift v5.1):
@propertyWrapper
struct NullEncodable<T>: Encodable where T: Encodable {
var wrappedValue: T?
init(wrappedValue: T?) {
self.wrappedValue = wrappedValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch wrappedValue {
case .some(let value): try container.encode(value)
case .none: try container.encodeNil()
}
}
}
Run Code Online (Sandbox Code Playgroud)
示例用法:
struct Tuplet: Encodable {
let a: String
let b: Int
@NullEncodable var c: String? = nil
}
struct Test: Encodable {
@NullEncodable var name: String? = nil
@NullEncodable var description: String? = nil
@NullEncodable var tuplet: Tuplet? = nil
}
var test = Test()
test.tuplet = Tuplet(a: "whee", b: 42)
test.description = "A test"
let data = try JSONEncoder().encode(test)
print(String(data: data, encoding: .utf8) ?? "")
Run Code Online (Sandbox Code Playgroud)
输出:
{
"name": null,
"description": "A test",
"tuplet": {
"a": "whee",
"b": 42,
"c": null
}
}
Run Code Online (Sandbox Code Playgroud)
完整实现在这里:https : //github.com/g-mark/NullCodable
| 归档时间: |
|
| 查看次数: |
5840 次 |
| 最近记录: |