Swift Codable:使用字典解析JSON?多个不同的条目?

时间:2018-10-29 14:04:59

标签: swift codable

我有一个很大的Json文件要解析(几MB),并且没有适当的文档。生活艰难。据我所知,文件很容易。但是有些事情我不理解,简单的Codable协议失败了。在数组layers中,我可以找到三个Layer对象,每个对象都有属性elements。在elements中,我可以找到多个elementData-在第一层中有"ImageID":32dd...component,在第二层和第三层"name":...和{{1} }。可能会有更多的可能性。我的每一次尝试都以错误结束。

很抱歉,这段代码会持续很长时间,但是我不想删掉重要的部分:

"contours": [...]

如何处理?

1 个答案:

答案 0 :(得分:2)

https://app.quicktype.io,正确的json

{ "layers":[
{
  "name":"img",
  "elements":[
    {
      "elementData":{
        "imageId":"32dd800000002"
      },
      "transform":{
        "xScale":100,
        "yScale":100
      },
      "active":true
    },
    {
      "component":{
        "glyphName":"e",
        "layerName":"img"
      }
    },
    {
      "elementData":{
        "composite":{
          "builder":{
            "builderGroup":{
            }
          }
        }
      },
      "transform":{
        "xOffset":120
      },
      "nonSpacing":true
    }
  ],
  "color":"maroon",
  "active":true
},
{
  "name":"Black",
  "elements":[
    {
      "component":{
        "glyphName":"e",
        "layerName":"Black"
      }
    },
    {
      "elementData":{
        "name":"caron",
        "contours":[
          {
            "nodes":[
              "80 577",
              "107 549  142 550  167 575 s"
            ]
          }
        ]
      }
    }
  ],
  "color":"#00802a"
},
{
  "name":"Thin",
  "elements":[
    {
      "component":{
        "glyphName":"e",
        "layerName":"Thin"
      }
    },
    {
      "elementData":{
        "name":"caron",
        "contours":[
          {
            "nodes":[
              "102 597 s",
              "118 580  132 580  148 597 s",
              "250 710",
              "235 726",
              "110 613",
              "140 613",
              "14 726",
              "-1 710"
            ]
          }
        ]
      }
    }
  ],
  "color":"#6a8000"
}
]}

解析

struct Welcome: Codable {
    let layers: [Layer]
}

struct Layer: Codable {
    let name: String
    let elements: [Element]
    let color: String
    let active: Bool?
}

struct Element: Codable {
    let elementData: ElementData?
    let transform: Transform?
    let active: Bool?
    let component: Component?
    let nonSpacing: Bool?
}

struct Component: Codable {
    let glyphName, layerName: String
}

struct ElementData: Codable {
    let imageID: String?
    let composite: Composite?
    let name: String?
    let contours: [Contour]?

    enum CodingKeys: String, CodingKey {
        case imageID = "imageId"
        case composite, name, contours
    }
}

struct Composite: Codable {
    let builder: Builder
}

struct Builder: Codable {
    let builderGroup: BuilderGroup
}

struct BuilderGroup: Codable {
}

struct Contour: Codable {
    let nodes: [String]
}

struct Transform: Codable {
    let xScale, yScale, xOffset: Int?
}

do {
     let res = try JSONDecoder().decode(Welcome.self,from:data)
 }
catch { 
    print(error)
}

编辑:

struct Welcome: Codable {
    let layers: [Layer]
}

struct Layer: Codable {
    let name: String
    let elements: [Element]
    let color: String
    let active: Bool?
}

struct Element: Codable {
    let elementData: ElementDataUnion?
    let transform: Transform?
    let active: Bool?
    let component: Component?
    let nonSpacing: Bool?
}

struct Component: Codable {
    let glyphName, layerName: String
}

enum ElementDataUnion: Codable {
    case elementDataClass(ElementDataClass)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(ElementDataClass.self) {
            self = .elementDataClass(x)
            return
        }
        throw DecodingError.typeMismatch(ElementDataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ElementDataUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .elementDataClass(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

struct ElementDataClass: Codable {
    let composite: Composite?
    let name: String?
    let contours: [Contour]?
}

struct Composite: Codable {
    let builder: Builder
}

struct Builder: Codable {
    let builderGroup: BuilderGroup
}

struct BuilderGroup: Codable {
}

struct Contour: Codable {
    let nodes: [String]
}

struct Transform: Codable {
    let xScale, yScale, xOffset: Int?
}

如果您需要elementData作为字典/字符串、、、、 elements作为数组/字符串,请使用

struct Welcome: Codable {
    let layers: [Layer]
}

struct Layer: Codable {
    let name: String
    let elements: Elements
    let color: String
    let active: Bool?
}

enum Elements: Codable {
    case elementArray([Element])
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode([Element].self) {
            self = .elementArray(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(Elements.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Elements"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .elementArray(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

struct Element: Codable {
    let component: Component?
    let elementData: ElementDataUnion?
}

struct Component: Codable {
    let glyphName, layerName: String
}

enum ElementDataUnion: Codable {
    case elementDataClass(ElementDataClass)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(ElementDataClass.self) {
            self = .elementDataClass(x)
            return
        }
        throw DecodingError.typeMismatch(ElementDataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ElementDataUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .elementDataClass(let x):
            try container.encode(x)
        case .string(let x):
            try container.encode(x)
        }
    }
}

struct ElementDataClass: Codable {
    let name: String
    let contours: [Contour]
}

struct Contour: Codable {
    let nodes: [String]
}