Swift Codable: разбор JSON со словарем? Несколько разных записей? - PullRequest
0 голосов
/ 29 октября 2018

У меня есть очень большой файл Json для анализа (несколько МБ) и нет соответствующей документации для одного. Жизнь тяжела. Насколько я могу распознать объекты в файле, это легко. Но есть кое-что, чего я не понимаю, и простой протокол Codable не работает. В массиве layers я могу найти три объекта Layer, каждый из которых имеет свойство elements. В elements я могу найти более одного elementData - в первом слое есть "ImageID":32dd... и component, во втором и третьем слоях "name":... и "contours": [...]. Вероятно, может быть больше возможностей. Каждая моя попытка заканчивается ошибкой.

Извините, этот код будет долгим, но я не хочу сокращать, возможно, важные части:

    "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"
    }
  ],

Как с этим бороться?

1 Ответ

0 голосов
/ 29 октября 2018

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]
}
...