Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在不覆盖文件的情况下将此数据正确写入JSON文件?_Json_Swift_Xcode_Codable_Nsdocumentdirectory - Fatal编程技术网

如何在不覆盖文件的情况下将此数据正确写入JSON文件?

如何在不覆盖文件的情况下将此数据正确写入JSON文件?,json,swift,xcode,codable,nsdocumentdirectory,Json,Swift,Xcode,Codable,Nsdocumentdirectory,我正在将一个JSON文件写入documents目录,我想将其保存在一个文件中,稍后再读取。结构如下所示: struct SymptomD:Codable { var symptom:String var severity:String var comment:String var timestamp:String } 然后我会这样写文档: var completeData = SymptomD(symptom: "", severity: "", comment: "", timestamp:

我正在将一个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)
         }
    }
}
有了这个,我只能添加一次数据,我不确定我应该如何向JSON文件添加另一个“行”,这样我就可以读取这些数据并用我的结构解码它们,以便以后在
tableView
中使用。生成的JSON文件如下所示:

struct SymptomD:Codable
{
var symptom:String
var severity:String
var comment:String
var timestamp:String
}

我可以用什么方法再次调用
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)
}
这将打印:

症状(症状:“焦虑”,严重程度:“轻度”, 注释:,时间戳:“1592356106.9662929”), __lldb_expr_532.症状(症状:“抑郁”,严重程度:“中度”,注释:,时间戳:“1592356106.978864”)]

编辑/更新:

如果需要更新JSON的一行,则需要使结构符合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
    }
}

我觉得这个问题很清楚。您正在将另一个词典附加到现有词典中,但您应该创建一个词典数组,以便能够将词典附加到该词典中

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)
}
这将打印:

症状(症状:“焦虑”,严重程度:“轻度”, 注释:,时间戳:“1592356106.9662929”), __lldb_expr_532.症状(症状:“抑郁”,严重程度:“中度”,注释:,时间戳:“1592356106.978864”)]

编辑/更新:

如果需要更新JSON的一行,则需要使结构符合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
    }
}

太好了,谢谢!使用此方法,我以后是否能够访问某些“行”并更改/更新/删除它们?或者我应该将它们添加到一个新的树下,比如通过它们的时间戳,这样我就可以找到并编辑它们了?谢谢我不认为你能在不覆盖整个文件的情况下编辑它们。如果让logFile=logFile,
条件绑定的初始值设定项必须具有可选类型,而不是“URL
,不确定原因。对于ios 13.0下的设备,由于seek不可用,我应该怎么做?我认为使用FileHandle没有任何好处。我认为您应该始终覆盖整个文件。@Petrrupper我添加了更新丢失的属性值的选项。非常感谢!使用此方法,我以后是否能够访问某些“行”并更改/更新/删除它们?或者我应该将它们添加到一个新的树下,比如通过它们的时间戳,这样我就可以找到并编辑它们了?谢谢我不认为你能在不覆盖整个文件的情况下编辑它们。如果让logFile=logFile,
条件绑定的初始值设定项必须具有可选类型,而不是“URL
,不确定原因。对于ios 13.0下的设备,由于seek不可用,我应该怎么做?我认为使用FileHandle没有任何好处。我认为您应该始终覆盖整个文件。@Petrrupper我添加了更新丢失的属性值的选项。