2

I have a very big Json file to (few MB) parse and no proper documentation for one. Life is hard. As far as I can recognise objects in file it's easy. But there is something I don't understand and simple Codable protocol fails. In array layers I can find three Layer objects, each of them has property elements. In elements I can find more than one elementData - in first Layer there are "ImageID":32dd... and component, in second and third Layer "name":... and "contours": [...]. Probably could be more possibilities. Each my try ends with error.

Sorry this code will by long, but I don't want to cut maybe important parts:

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

How to deal with that?

1 Answer 1

5

https://app.quicktype.io , correct 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"
}
]}

Parse

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)
}

Edit:

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?
}

If you need elementData to be dictionary/string ,,,,, elements to be array/string then use

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]
}
Sign up to request clarification or add additional context in comments.

5 Comments

Ach. From some reasons I tried to keep img and contours in separated objects. Thank you!
I still have the same error I had: typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "layers", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "elements", intValue: nil), _JSONKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue: "elementData", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil)). How to live with that?
** elementData** should dictionary not string post you possible return values of it
In fact THIS is my question: How to deal with this dictionary?
see edit it'll work if elementData it a string/dictionary

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.