быстрое декодирование полиморфный ответ JSON - PullRequest
0 голосов
/ 17 мая 2019

Здравствуйте, я хочу декодировать этот json.

"interest_point":{
    "id": 2,
    "name": "Panoramic",
    "description": "On vous propose ....",
    "pointable_type": "ConferenceInterestPoint",
    "pointable_id": 1,
    "working_time": [],
    "pointable": {
        "id": 1,
        "surface": 354.56,
        "capacity": "140",
        "price": 500,
        "price_unit": "HOURS",
    },
    "comments": [],
}
"interest_point":{
    "id": 5,
    "name": "Carte",
    "description": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "pointable_type": "RestaurantInterestPoint",
    "pointable_id": 2,
    "working_time": [],
    "pointable": {
        "id": 2,
        "type": "CARTE",
        "smocking_area": 1,
    },
    "comments": []

}

поле "pointable" является дочерним классом интереса_point с типом, зависящим от "pointable_type". Я хочу динамически декодировать ответ вхороший ChildType of InterestPoint

1 Ответ

0 голосов
/ 21 мая 2019

Возможно, у вас есть следующий класс, описывающий корень вашего JSON:

final class Root: Codable {
    let interestPoint: InterestPoint?

    private enum CodingKeys: String, CodingKey {
        case interestPoint = "interest_point"
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        interestPoint = try values.decodeIfPresent(InterestPoint.self, forKey: .interestPoint)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(interestPoint, forKey: .interestPoint)
    }
}

Как я могу догадаться, у вас есть BaseInterestPoint, который служит базовым классом для InterestPoint, ConferenceInterestPoint и RestaurantInterestPoint:

class BaseInterestPoint: Codable {
    let id: Int?

    private enum CodingKeys: String, CodingKey {
        case id
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decodeIfPresent(Int.self, forKey: .id)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
    }
}

ConferenceInterestPoint и RestaurantInterestPoint:

final class ConferenceInterestPoint: BaseInterestPoint {
    let surface: Double?
    let capacity: String?
    let price: Int?
    let priceUnit: String?

    private enum CodingKeys: String, CodingKey {
        case surface
        case capacity
        case price
        case priceUnit = "price_unit"
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        surface = try values.decodeIfPresent(Double.self, forKey: .surface)
        capacity = try values.decodeIfPresent(String.self, forKey: .capacity)
        price = try values.decodeIfPresent(Int.self, forKey: .price)
        priceUnit = try values.decodeIfPresent(String.self, forKey: .priceUnit)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(surface, forKey: .surface)
        try container.encode(capacity, forKey: .capacity)
        try container.encode(price, forKey: .price)
        try container.encode(priceUnit, forKey: .priceUnit)
    }
}

final class RestaurantInterestPoint: BaseInterestPoint {
    let type: String?
    let smockingArea: Int?

    private enum CodingKeys: String, CodingKey {
        case type
        case smockingArea = "smocking_area"
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        type = try values.decodeIfPresent(String.self, forKey: .type)
        smockingArea = try values.decodeIfPresent(Int.self, forKey: .smockingArea)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(type, forKey: .type)
        try container.encode(smockingArea, forKey: .smockingArea)
    }
}

Для решения проблемы с полиморфными объектами используйте enum:

enum Pointable {
    case conferenceInterestPoint(ConferenceInterestPoint?)
    case restaurantInterestPoint(RestaurantInterestPoint?)
}

В такихВ случае, если вы получите следующее InterestPoint:

final class InterestPoint: BaseInterestPoint {
    let name: String?
    let description: String?
    let pointableType: String?
    let pointableId: Int?
    let workingTime: [Int]?
    let pointable: Pointable?
    let comments: [String]?

    private enum CodingKeys: String, CodingKey {
        case name
        case description
        case pointableType = "pointable_type"
        case pointableId = "pointable_id"
        case workingTime = "working_time"
        case pointable
        case comments
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decodeIfPresent(String.self, forKey: .name)
        description = try values.decodeIfPresent(String.self, forKey: .description)
        pointableType = try values.decodeIfPresent(String.self, forKey: .pointableType)
        pointableId = try values.decodeIfPresent(Int.self, forKey: .pointableId)
        workingTime = try values.decodeIfPresent([Int].self, forKey: .workingTime)
        if pointableType == "ConferenceInterestPoint" {
            pointable = .conferenceInterestPoint(try values.decodeIfPresent(ConferenceInterestPoint.self, forKey: .pointable))
        } else if pointableType == "RestaurantInterestPoint" {
            pointable = .restaurantInterestPoint(try values.decodeIfPresent(RestaurantInterestPoint.self, forKey: .pointable))
        } else {
            pointable = nil
        }
        comments = try values.decodeIfPresent([String].self, forKey: .comments)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(description, forKey: .description)
        try container.encode(pointableType, forKey: .pointableType)
        try container.encode(pointableId, forKey: .pointableId)
        try container.encode(workingTime, forKey: .workingTime)
        if let pointable = pointable {
            switch pointable {
            case .conferenceInterestPoint(let conferenceInterestPoint):
                if let conferenceInterestPoint = conferenceInterestPoint {
                    try container.encode(conferenceInterestPoint, forKey: .pointable)
                }
            case .restaurantInterestPoint(let restaurantInterestPoint):
                if let restaurantInterestPoint = restaurantInterestPoint {
                    try container.encode(restaurantInterestPoint, forKey: .pointable)
                }
            }
        }
        try container.encode(comments, forKey: .comments)
    }
}

Весь код в этом посте был проверен в Xcode 10.2.1.

...