如何将嵌套的JSON文件解码为具有未拥有属性的swift类

时间:2019-08-12 23:52:48

标签: json swift codable

我有一个自定义模型,其中包含多个类,这些类是我在Swift中为基于文档的Mac应用程序编写的。当用户向其文档输入数据时,将创建一个类Itinerary的实例并将其存储在一个数组中,该数组是另一个类Course的另一个“根”对象的属性。最重要的是,可以创建多个课程,并将它们存储在应用程序的Document类的数组中。我希望用户能够保存他们的数据并在以后重新加载它。所以我试图在我的自定义类型上实现Codable协议。

可编码的一致性很好;当我尝试保存时,将生成所需的JSON文件。问题出在解码时。

在尝试对每个路线进行解码时,需要在初始化结束之前设置其course属性,因为它被定义为指向其所属路线的无主var。发生这种情况的地方位于课程的addItinerary(_:)函数中,该函数直接在行程的指定初始值设定项中调用。这意味着我还需要将课程传递给行程的指定init。这就是问题所在……当我尝试从可解码的初始值设定项中重新初始化行程时,我无权访问需要添加行程的过程,因此无法在设置过程中设置course属性初始化。

问题似乎围绕着我如何尝试初始化路线以及我如何尝试管理内存。一项研究表明,我的方向是将课程属性更改为可选的弱变量(即weak var course: Course?或将其标记为隐式未包装的可选变量,即var course: Course!)。这两个选项似乎都可能在模型中引入了不必要的复杂性,因为我将需要重写行程初始化器。根据Apple的文档,将某个属性标记为无主when the other instance has the same lifetime or a longer lifetime是适当的,这可以在此处准确地描述这种情况。

我的问题有两个方面。尝试初始化模型似乎存在一个固有缺陷:

  • 如果没有,解码时如何重新组装模型?我需要重新考虑初始化逻辑吗?
  • 如果是这样,有什么缺陷?我是否使用正确的ARC策略来管理内存?我应该考虑使用JSON以外的其他数据结构吗?

为了简洁起见,我从似乎无关的类中排除了所有属性。但是,如果人们认为有必要提供完整的解决方案,我将提供更多代码。

注意事项的简明注释:

  • 所需的JSON是在不对Itinerary类的course属性进行编码的情况下产生的。
  • 课程的addItinerary(_:)函数是在设置了行程的course属性时……在行程的指定init中调用。
  • 但是...在解码时无法调用该init,因为我从未对课程进行编码...当我尝试在行程的encode(to:)中对课程进行编码时,Xcode会抛出BAD ACCESS错误,并且指向我不确定如何调试的内存中的地址。

    class Document: NSDocument {
        var masterCourseList = [Course]()
    }
    
    class Course: NSObject {
        // ...other properties
        private(set) var itineraries: [Itinerary]() {
            didSet {
                // sorting code
            }
        }
    
        func addItinerary(_ itinerary: Itinerary) {
            itinerary.course = self
            self.itineraries.append(itinerary)
        }
    
        // encodable conformance
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            // ...encode other properties
            try container.encode(itineraries, forKey: .itineraries)
        }
    
        // decodable conformance
        required convenience init decode(from: Decoder) throws {
            let decoder = try decoder.container(keyedBy: CodingKeys.self)
            // ...decode other properties
            let decodedItineraries = container.decode(Array<Itinerary>.self, forKey: .itineraries)
    
            self.init()
    
            for itinerary in decodedItineraries {
                self.addItinerary(itinerary)
            }
        }
    
        enum CodingKeys: CodingKey {
            case itineraries, // other keys... //
        }
    }
    
    
    class Itinerary: NSObject {
        @objc dynamic let date: Date
        unowned var course: Course
        // ...other properties
    
        private init(date: Date, course: Course) {
            self.course = course
            self.date = date
            super.init()
            course.addItinerary(self)
        }
    
        // encodable conformance
        func encode(to encoder: Encoder) throws {
            // ...encode all the desired properties of itinerary class
            // ...never encoded the 'course' property but still got the desired JSON file
        }
    
        // decodable conformance
        required convenience init(from decoder: Decoder) throws {
            // ...decode all the properties
            // ...never decoded the 'course' property because it was never encoded
    
            self.init(date: decodedDate, course: decodedCourse)
        }
    }
    

还有正在生成的JSON示例...

[
  {
    "itineraries" : [
      {
        "date" : 587357150.51135898
      },
      {
        "date" : 587443563.588081
      }
    ],
    "title" : "sample course 1",
    "startDate" : 587357146.98558402,
    "endDate" : 587789146.98558402
  },
  {
    "itineraries" : [
      {
        "date" : 587357150.51135898
      },
      {
        "date" : 587443563.588081
      },
      {
        "date" : 587443563.588081
      }
    ],
    "title" : "sample course 2",
    "startDate" : 587957146.98558402,
    "endDate" : 588389146.98558402
  }
]

0 个答案:

没有答案