如何在不覆盖文件的情况下将此数据正确写入 JSON 文件?
How do I correctly write this data to a JSON file without overwriting the file?
我正在写一个 JSON 文件到 documents 目录,我想把它保存在一个文件中,以后再读。该结构如下所示:
struct SymptomD:Codable
{
var symptom:String
var severity:String
var comment:String
var timestamp:String
}
然后我这样写文档:
var completeData = SymptomD(symptom: "", severity: "", comment: "", timestamp: "")
func writeTrackedSymptomValues(symptom: String, comment: String, time: String, timestamp: String) {
completeData.symptom = symptom
completeData.severity = self.severity
completeData.comment = comment
completeData.timestamp = timestamp
createJSON()
}
var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = "symptom_data.json"
return documentsDirectory.appendingPathComponent(fileName)
}
func createJSON() {
guard let logFile = logFile else {
return
}
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(completeData) //This does not work, I am not sure how to add data without overwriting the previous file.
fileHandle.closeFile()
}
} else {
do {
try JSONEncoder().encode(completeData)
.write(to: logFile)
} catch {
print(error)
}
}
}
有了这个我只能添加一次数据,我不确定我应该如何添加另一个 'row' 基本上到 JSON 文件,这样我就可以读取这些数据并解码它们与我的结构一起用于 tableView
之后。制作的 JSON 文件如下所示:
有什么方法可以再次调用 createJSON
函数而不覆盖整个文件,我应该如何组织它以便在读取 JSON 时可以对其进行解码简单地访问信息。
更新:
使用它我可以向 JSON、
添加更多行
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(jsonData)
fileHandle.closeFile()
}
给我这个:
{"timestamp":"1592341465","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592342433","comment":"","severity":"Moderate","symptom":"Anxiety"}{"timestamp":"1592342458","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592343853","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592329440","comment":"","severity":"Mild","symptom":"Fatigue"}{"timestamp":"1592344328","comment":"","severity":"Mild","symptom":"Mood Swings"}{"timestamp":"1592257920","comment":"test","severity":"Mild","symptom":"Anxiety"}
但是当试图解析这个时,它崩溃并出现错误:
Code=3840 "Garbage at end."
我做错了什么?
这个问题在我看来很清楚。您正在将另一个字典附加到现有字典,但您应该创建一个字典数组以便能够将字典附加到它。
struct SymptomD: Codable {
var symptom, severity, comment, timestamp: String
init(symptom: String = "", severity: String = "", comment: String = "", timestamp: String = "") {
self.symptom = symptom
self.severity = severity
self.comment = comment
self.timestamp = timestamp
}
}
如果您想手动将文本附加到您的 json 字符串中,您需要查找文件末尾之前的位置,在下一个 json 对象之前添加一个逗号,然后它后面的一个右括号:
extension SymptomD {
func write(to url: URL) throws {
if FileManager.default.fileExists(atPath: url.path) {
let fileHandle = try FileHandle(forWritingTo: url)
try fileHandle.seek(toOffset: fileHandle.seekToEndOfFile()-1)
let data = try JSONEncoder().encode(self)
fileHandle.write(Data(",".utf8) + data + Data("]".utf8))
fileHandle.closeFile()
} else {
try JSONEncoder().encode([self]).write(to: url)
}
}
}
游乐场测试:
var logFile: URL? {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("symptom_data.json")
}
var symptomD = SymptomD()
symptomD.symptom = "Anxiety"
symptomD.severity = "Mild"
symptomD.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD.write(to: logFile)
}
} catch {
print(error)
}
var symptomD2 = SymptomD()
symptomD2.symptom = "Depression"
symptomD2.severity = "Moderate"
symptomD2.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD2.write(to: logFile)
}
} catch {
print(error)
}
do {
if let logFile = logFile {
let symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: logFile))
print(symptoms)
}
} catch {
print(error)
}
这将打印:
[__lldb_expr_532.SymptomD(symptom: "Anxiety", severity: "Mild",
comment: "", timestamp: "1592356106.9662929"),
__lldb_expr_532.SymptomD(symptom: "Depression", severity: "Moderate", comment: "", timestamp: "1592356106.978864")]
edit/update:
如果您需要更新 JSON 中的单个 "row",您需要使您的结构符合 equatable,阅读您的集合并找到它的索引:
extension SymptomD: Equatable {
static func ==(lhs: SymptomD, rhs: SymptomD) {
(lhs.symptom, lhs.severity, lhs.comment ,lhs.timestamp) ==
(rhs.symptom, rhs.severity, rhs.comment ,rhs.timestamp)
}
@discardableResult
mutating func updateAndWrite(symptom: String? = nil, severity: String? = nil, comment: String? = nil, timestamp: String? = nil, at url: URL) throws -> [SymptomD]? {
var symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: url))
if let index = symptoms.firstIndex(of: self) {
self.symptom = symptom ?? self.symptom
self.severity = severity ?? self.severity
self.comment = comment ?? self.comment
self.timestamp = timestamp ?? self.timestamp
symptoms[index] = self
try JSONEncoder().encode(symptoms).write(to: url, options: .atomic)
return symptoms
}
return nil
}
}
我正在写一个 JSON 文件到 documents 目录,我想把它保存在一个文件中,以后再读。该结构如下所示:
struct SymptomD:Codable
{
var symptom:String
var severity:String
var comment:String
var timestamp:String
}
然后我这样写文档:
var completeData = SymptomD(symptom: "", severity: "", comment: "", timestamp: "")
func writeTrackedSymptomValues(symptom: String, comment: String, time: String, timestamp: String) {
completeData.symptom = symptom
completeData.severity = self.severity
completeData.comment = comment
completeData.timestamp = timestamp
createJSON()
}
var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = "symptom_data.json"
return documentsDirectory.appendingPathComponent(fileName)
}
func createJSON() {
guard let logFile = logFile else {
return
}
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(completeData) //This does not work, I am not sure how to add data without overwriting the previous file.
fileHandle.closeFile()
}
} else {
do {
try JSONEncoder().encode(completeData)
.write(to: logFile)
} catch {
print(error)
}
}
}
有了这个我只能添加一次数据,我不确定我应该如何添加另一个 'row' 基本上到 JSON 文件,这样我就可以读取这些数据并解码它们与我的结构一起用于 tableView
之后。制作的 JSON 文件如下所示:
有什么方法可以再次调用 createJSON
函数而不覆盖整个文件,我应该如何组织它以便在读取 JSON 时可以对其进行解码简单地访问信息。
更新:
使用它我可以向 JSON、
添加更多行 let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(jsonData)
fileHandle.closeFile()
}
给我这个:
{"timestamp":"1592341465","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592342433","comment":"","severity":"Moderate","symptom":"Anxiety"}{"timestamp":"1592342458","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592343853","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592329440","comment":"","severity":"Mild","symptom":"Fatigue"}{"timestamp":"1592344328","comment":"","severity":"Mild","symptom":"Mood Swings"}{"timestamp":"1592257920","comment":"test","severity":"Mild","symptom":"Anxiety"}
但是当试图解析这个时,它崩溃并出现错误:
Code=3840 "Garbage at end."
我做错了什么?
这个问题在我看来很清楚。您正在将另一个字典附加到现有字典,但您应该创建一个字典数组以便能够将字典附加到它。
struct SymptomD: Codable {
var symptom, severity, comment, timestamp: String
init(symptom: String = "", severity: String = "", comment: String = "", timestamp: String = "") {
self.symptom = symptom
self.severity = severity
self.comment = comment
self.timestamp = timestamp
}
}
如果您想手动将文本附加到您的 json 字符串中,您需要查找文件末尾之前的位置,在下一个 json 对象之前添加一个逗号,然后它后面的一个右括号:
extension SymptomD {
func write(to url: URL) throws {
if FileManager.default.fileExists(atPath: url.path) {
let fileHandle = try FileHandle(forWritingTo: url)
try fileHandle.seek(toOffset: fileHandle.seekToEndOfFile()-1)
let data = try JSONEncoder().encode(self)
fileHandle.write(Data(",".utf8) + data + Data("]".utf8))
fileHandle.closeFile()
} else {
try JSONEncoder().encode([self]).write(to: url)
}
}
}
游乐场测试:
var logFile: URL? {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("symptom_data.json")
}
var symptomD = SymptomD()
symptomD.symptom = "Anxiety"
symptomD.severity = "Mild"
symptomD.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD.write(to: logFile)
}
} catch {
print(error)
}
var symptomD2 = SymptomD()
symptomD2.symptom = "Depression"
symptomD2.severity = "Moderate"
symptomD2.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD2.write(to: logFile)
}
} catch {
print(error)
}
do {
if let logFile = logFile {
let symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: logFile))
print(symptoms)
}
} catch {
print(error)
}
这将打印:
[__lldb_expr_532.SymptomD(symptom: "Anxiety", severity: "Mild", comment: "", timestamp: "1592356106.9662929"), __lldb_expr_532.SymptomD(symptom: "Depression", severity: "Moderate", comment: "", timestamp: "1592356106.978864")]
edit/update:
如果您需要更新 JSON 中的单个 "row",您需要使您的结构符合 equatable,阅读您的集合并找到它的索引:
extension SymptomD: Equatable {
static func ==(lhs: SymptomD, rhs: SymptomD) {
(lhs.symptom, lhs.severity, lhs.comment ,lhs.timestamp) ==
(rhs.symptom, rhs.severity, rhs.comment ,rhs.timestamp)
}
@discardableResult
mutating func updateAndWrite(symptom: String? = nil, severity: String? = nil, comment: String? = nil, timestamp: String? = nil, at url: URL) throws -> [SymptomD]? {
var symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: url))
if let index = symptoms.firstIndex(of: self) {
self.symptom = symptom ?? self.symptom
self.severity = severity ?? self.severity
self.comment = comment ?? self.comment
self.timestamp = timestamp ?? self.timestamp
symptoms[index] = self
try JSONEncoder().encode(symptoms).write(to: url, options: .atomic)
return symptoms
}
return nil
}
}