英文:
Swift custom encode for encoding Int time like pretty string
问题
I have a custom object stored in a Struct:
struct Event {
let type: String
let value: String
let time: Int
}
其中,time 是以毫秒为单位的 Unix 时间。在应用程序中,我有一个可滚动的文本字段,当我以 JSON 格式打印此事件时:
{
"type" : "rain",
"value" : "0.5",
"time" : 1681663944
}
但是对于 time 参数,我想将 Unix 时间转换为人类可读的形式(即 16/04/2023),因此我需要自定义该参数的编码方式。问题是,所有我的对象都符合此协议,因此我可以免费使用Codable
的toJsonString()
方法。
protocol BaseObject: Codable {
}
extension BaseObject {
func toJsonString() -> String {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(self)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
return json ?? ""
} catch {
return ""
}
}
}
因此,我不需要为每个对象单独编写 toJsonString()
方法。天真的解决方案应该是为每个对象编写自定义的 toJsonString()
,但我有很多结构体,如果我找到了一个巧妙的方法来拦截 time
属性,我可以用这种类型覆盖应用程序中的所有属性的编码表示。
有什么想法吗?
谢谢。
编辑
目前,我找到了这个解决方案:
func toJsonString() -> String {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(self)
var modData = Data()
if var dict = jsonData.toDictionary() {
dict.keys.filter{ $0.contains("time") }.forEach { key in
if let t = dict[key] as? Int {
dict[key] = t.toStringDate()
}
}
modData = dict.toData()
}
let json = String(data: modData, encoding: String.Encoding.utf8)
return json ?? ""
} catch {
return ""
}
}
英文:
I have a custom object stored in a Struct:
struct Event {
let type: String
let value: String
let time: Int
}
where time is a UnixTime in millis. In the App I have a scrollable text field when I print this event in json format
{
"type" : "rain",
"value" : "0.5",
"time" : 1681663944
}
but for time parameter I would like to convert the UnixTime in a human readable form (i.e. 16/04/2023), so I need to customize haw this parameter is encoded. The problem is that all my objects are conforms to this protocol, so I have the toJsonString()
method for free, based on the power of Codable
.
protocol BaseObject: Codable {
}
extension BaseObject {
func toJsonString() -> String {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(self)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
return json ?? ""
} catch {
return ""
}
}
}
So, I don't have a toJsonString()
method for each object separately. Naïve solution should be write a custom toJsonString()
for each object, but I have a lot of struct and if I found a smart way to intercept time
attributes I can override the encode representation of all attributes in the App with this type.
Ideas ?
Thanks.
EDIT
For now I found this solution:
func toJsonString() -> String {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(self)
var modData = Data()
if var dict = jsonData.toDictionary() {
dict.keys.filter{ $0.contains("time") }.forEach { key in
if let t = dict[key] as? Int {
dict[key] = t.toStringDate()
}
}
modData = dict.toData()
}
let json = String(data: modData, encoding: String.Encoding.utf8)
return json ?? ""
} catch {
return ""
}
}
答案1
得分: 1
你可以使用属性包装器来为特定属性提供自定义编码行为:
import Foundation
@propertyWrapper
struct PrettyEncodedDate {
static let formatter = {
let df = DateFormatter()
// 自定义日期格式化程序
df.dateStyle = .full
return df
}()
let wrappedValue: Date
}
extension PrettyEncodedDate: Decodable {
struct InvalidDateError: Error {
let dateString: String
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.formatter.date(from: dateString) else {
throw InvalidDateError(dateString: dateString)
}
self.init(wrappedValue: date)
}
}
extension PrettyEncodedDate: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let formattedDate = Self.formatter.string(from: self.wrappedValue)
try container.encode(formattedDate)
}
}
这是一个示例用法:
struct Event: Codable {
let type: String
let value: String
@PrettyEncodedDate var time: Date
}
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let event = Event(type: "some type", value: "some value", time: Date.now)
let prettyJSON = try encoder.encode(event)
print(String(data: prettyJSON, encoding: .utf8)!)
这将打印:
{
"type" : "some type",
"value" : "some value",
"time" : "Sunday, April 16, 2023"
}
这依赖于 time
是一个 Date
(它可能应该是的)。如果必须保持为 Int
(虽然我建议不要这样做),你可以调整属性包装器以更改当前的行为(String -> Date
,Date -> String
)以添加额外的处理步骤(String -> Date -> Int
,Int -> Date -> String
)。
英文:
You can use a property wrapper to hook in and give custom encoding behaviour for a specific property:
import Foundation
@propertyWrapper
struct PrettyEncodedDate {
static let formatter = {
let df = DateFormatter()
// Customize the date formatter however you'd like
df.dateStyle = .full
return df
}()
let wrappedValue: Date
}
extension PrettyEncodedDate: Decodable {
struct InvalidDateError: Error {
let dateString: String
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.formatter.date(from: dateString) else {
throw InvalidDateError(dateString: dateString)
}
self.init(wrappedValue: date)
}
}
extension PrettyEncodedDate: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let formattedDate = Self.formatter.string(from: self.wrappedValue)
try container.encode(formattedDate)
}
}
Here's an example usage:
struct Event: Codable {
let type: String
let value: String
@PrettyEncodedDate var time: Date
}
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let event = Event(type: "some type", value: "some value", time: Date.now)
let prettyJSON = try encoder.encode(event)
print(String(data: prettyJSON, encoding: .utf8)!)
Which prints:
{
"type" : "some type",
"value" : "some value",
"time" : "Sunday, April 16, 2023"
}
This relies on time
to be a Date
(which it probably should be). If it must stay an Int
(though I'd caution against that), you can tweak the property wrapper to change the current behaviour (String -> Date
, Date -> String
) to add an extra processing step (String -> Date -> Int
, Int -> Date -> String
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论