0

I have the following structure for a Json:

"config": {
    "ids": false,
    "names": [
      {
        "name": "value1"
      },
      {
        "name": "value2"
      }
    ]
  }

The issue that I have is that the response can have the following format too:

"config": {
    "ids": [
      {
        "id": "id1"
      },
      {
        "id": "id2"
      }
    ],
    "names": false
  }

Any suggestion of how can I achieve this?

Current state:

struct Config: Decodable, Equatable {
    let names: Names?
}

struct Names: Decodable, Equatable {
    let names: [[String: String]]?
    
    init(from decoder: Decoder) throws {
        facets = try container.decode([[String: String]].self, forKey: ???)
    }
}
George
  • 328
  • 2
  • 8
  • use a different struct for each configuration type. Right before you serialize the json into the struct, perform some kind of check to see which config the json has. – Tad Jan 25 '22 at 07:08

1 Answers1

1

Object Structure

Try and give the following a go:

// MARK: - Config
struct Config: Codable {
    let ids: IDS
    let names: Names
}

enum IDS: Codable {
    case bool(Bool)
    case idArray([ID])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Bool.self) {
            self = .bool(x)
            return
        }
        if let x = try? container.decode([ID].self) {
            self = .idArray(x)
            return
        }
        throw DecodingError.typeMismatch(IDS.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for IDS"))
    }

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

// MARK: - ID
struct ID: Codable {
    let id: String
}

enum Names: Codable {
    case bool(Bool)
    case nameArray([Name])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Bool.self) {
            self = .bool(x)
            return
        }
        if let x = try? container.decode([Name].self) {
            self = .nameArray(x)
            return
        }
        throw DecodingError.typeMismatch(Names.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Names"))
    }

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

// MARK: - Name
struct Name: Codable {
    let name: String
}

How I Did It

I generated this using QuickType which can convert JSON into Swift objects. In order to achieve the following above, I entered the JSON as a list of Config options and then just removed the parts I didn't need from the given output...

[
    {
        "config": {
        "ids": false,
        "names": [
          {
            "name": "value1"
          },
          {
            "name": "value2"
          }
        ]
      }
    },
    {
      "config": {
        "ids": [
          {
            "id": "id1"
          },
          {
            "id": "id2"
          }
        ],
        "names": false
      }
    }
]
Euan Traynor
  • 420
  • 2
  • 11