带有递归枚举的Swift Codable协议 [英] Swift Codable protocol with recursive enums

查看:288
本文介绍了带有递归枚举的Swift Codable协议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有一个像下面这样的模型,它使我可以构建Foo对象树。

Let's say that I have a model like the following, which allows me to build a tree of Foo objects.

struct Foo {

    var kind : Kind

    enum Kind {
        case node([Foo])
        case leaf
    }
}

如何使此Codable成为专用于 case节点( [Foo])

How can I make this Codable, specifically for the case node([Foo])?

推荐答案

这是基于@PauloMattos的答案的最终结构:

Here's the final struct, based on the answer from @PauloMattos:

基本Foo结构:

struct Foo {

    var name: String
    var kind: Kind

    enum Kind {
        case node([Foo])
        case leaf
    }

    init(name: String, kind: Kind) {
        self.name = name
        self.kind = kind
    }
}

可编码协议扩展:

extension Foo : Codable {

    enum CodingKeys: String, CodingKey {
        case name
        case nodes
    }

    enum CodableError: Error {
        case decoding(String)
        case encoding(String)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        switch kind {
        case .node(let nodes):
            var array = container.nestedUnkeyedContainer(forKey: .nodes)
            try array.encode(contentsOf: nodes)
            break
        case .leaf:
            break
        }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        // Assumes name exists for all objects
        if let name = try? container.decode(String.self, forKey: .name) {
            self.name = name
            self.kind = .leaf
            if let array = try? container.decode([Foo].self, forKey: .nodes) {
                self.kind = .node(array)
            }
            return
        }
        throw CodableError.decoding("Decoding Error")
    }
}

CustomStringConvertable协议扩展(从树中输出字符串):

CustomStringConvertable Protocol extension (to output string from the tree):

extension Foo : CustomStringConvertible {

    var description: String {
        return stringDescription(self)
    }

    private func stringDescription(_ foo: Foo) -> String {
        var string = ""
        switch foo.kind {
        case .leaf:
            return foo.name
        case .node(let nodes):
            string += "\(foo.name): ("
            for i in nodes.indices {
                string += stringDescription(nodes[i])
                // Comma seperate all but the last
                if i < nodes.count - 1 { string += ", " }
            }
            string += ")"
        }
        return string
    }
}

示例测试代码:

let a = Foo(name: "A", kind: .leaf)
let b = Foo(name: "B", kind: .leaf)
let c = Foo(name: "C", kind: .leaf)
let d = Foo(name: "D", kind: .node([b, c]))
let root = Foo(name: "ROOT", kind: .node([a, d]))

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try! encoder.encode(root)
let json = String(data: jsonData, encoding: .utf8)!
print("Foo to JSON:")
print(json)

let decoder = JSONDecoder()
do {
    let foo = try decoder.decode(Foo.self, from: jsonData)
    print("JSON to Foo:")
    print(foo)
} catch {
    print(error)
}

输出:

Foo to JSON:
{
  "name" : "ROOT",
  "nodes" : [
    {
      "name" : "A"
    },
    {
      "name" : "D",
      "nodes" : [
        {
          "name" : "B"
        },
        {
          "name" : "C"
        }
      ]
    }
  ]
}
JSON to Foo:
ROOT: (A, D: (B, C))

这篇关于带有递归枚举的Swift Codable协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆