XMLCoder icon indicating copy to clipboard operation
XMLCoder copied to clipboard

Is there support for Heterogeneous collections?

Open mhs2342 opened this issue 5 years ago • 4 comments

I'd love to use this library, just want to make sure there is support for heterogeneous collections before I begin integrating it.

I am getting a response back from an API that is serving two different kinds of responses and I need to be able to decode them both and put them into a single container. I could used a super class but then I would lose all the access to other properties. My original thought is that this might be able to be done with a Protocol of some sort?

Vehicle 1:

<LatestInfoTable>
    <AgencyVehicleName>1921</AgencyVehicleName>
    <RouteShortName>HWB</RouteShortName>
    <BlockID>c8bdfbe2-72e9-4870-81a6-d7cb8cd4263f</BlockID>
    <TripID>e15aed4d-f527-47a7-a4d5-f44fa59838de</TripID>
    <IsTripper>N</IsTripper>
    <PatternName>Hethwood B</PatternName>
    <TripStartTime>2019-04-17T12:45:00-04:00</TripStartTime>
    <LastStopName>Torgersen Hall</LastStopName>
    <StopCode>1114</StopCode>
    <Rank>0</Rank>
    <IsBusAtStop>Y</IsBusAtStop>
    <IsTimePoint>Y</IsTimePoint>
    <LatestEvent>2019-04-17T12:46:35-04:00</LatestEvent>
    <LatestRSAEvent>2019-04-17T12:46:31-04:00</LatestRSAEvent>
    <Latitude>50.22935</Latitude>
    <Longitude>-74.42052</Longitude>
    <Direction>308</Direction>
    <Speed>1</Speed>
    <TotalCount>0</TotalCount>
    <PercentOfCapacity>0</PercentOfCapacity>
</LatestInfoTable>

Vehicle 2:

<LatestInfoTable>
    <AgencyVehicleName>6412</AgencyVehicleName>
    <LatestEvent>2019-04-17T09:33:45-04:00</LatestEvent>
    <Latitude>50.19622</Latitude>
    <Longitude>-74.39557</Longitude>
    <Direction>252</Direction>
    <Speed>6</Speed>
</LatestInfoTable>

I'd like to be able to have something like this

struct Resonse: Decodable {
    var vehicles: [Vehicles] 

    enum CodingKeys: String, CodingKey {
        var vehicles = "LatestInfoTable"
    }
}

mhs2342 avatar Apr 17 '19 19:04 mhs2342

Hi @mhs2342, thank you for considering XMLCoder!

I see multiple options here. Would any of these fit your use case well?

  1. Decode as a dictionary:
let decoder = XMLDecoder()

let decoded1 = try decoder.decode([String: String].self, from: vehicle1)
let decoded2 = try decoder.decode([String: String].self, from: vehicle2)
  1. Decode to one struct with optional properties:
struct OptionalProperties: Decodable {
    let agencyVehicleName: String
    let latestEvent: String
    let latitude: Double
    let longitude: Double
    let direction: Int
    let speed: Int
    let routeShortName: String?
    let blockID: String?
    let isTripper: Bool?
    let patternName: String?
    let tripStartTime: String?
    let lastStopName: String?
    let stopCode: Int?
    let isBusAtStop: Bool?
    let isTimePoint: Bool?
    let latestRSAEvent: String?
    let totalCount: Int?
    let percentOfCapacity: Int?
}

let decoder = XMLDecoder()
decoder.keyDecodingStrategy = .convertFromCapitalized

let decoded1 = try decoder.decode(OptionalProperties.self, from: vehicle1)
let decoded2 = try decoder.decode(OptionalProperties.self, from: vehicle2)
  1. Decode to multiple structs with non-optional properties:
struct VehicleProperties: Decodable {
    let agencyVehicleName: String
    let latestEvent: String
    let latitude: Double
    let longitude: Double
    let direction: Int
    let speed: Int
}

struct BusProperties: Decodable {
    let routeShortName: String
    let blockID: String
    let isTripper: Bool
    let patternName: String
    let tripStartTime: String
    let lastStopName: String
    let stopCode: Int
    let isBusAtStop: Bool
    let isTimePoint: Bool
    let latestRSAEvent: String
    let totalCount: Int
    let percentOfCapacity: Int
}

let decoder = XMLDecoder()
decoder.keyDecodingStrategy = .convertFromCapitalized

let vehicle = try decoder.decode(VehicleProperties.self, from: vehicle1)
let bus = try decoder.decode(BusProperties.self, from: vehicle1)
  1. Decode to multiple structs with a parent struct:
struct VehicleProperties: Decodable {
    let agencyVehicleName: String
    let latestEvent: String
    let latitude: Double
    let longitude: Double
    let direction: Int
    let speed: Int
}

struct BusProperties: Decodable {
    let routeShortName: String
    let blockID: String
    let isTripper: Bool
    let patternName: String
    let tripStartTime: String
    let lastStopName: String
    let stopCode: Int
    let isBusAtStop: Bool
    let isTimePoint: Bool
    let latestRSAEvent: String
    let totalCount: Int
    let percentOfCapacity: Int
}

struct AllProperties: Decodable {
    let vehicle: VehicleProperties
    let bus: BusProperties?

    init(from decoder: Decoder) throws {
        let vehicleContainer = try decoder.singleValueContainer()
        let busContainer = vehicleContainer

        vehicle = try vehicleContainer.decode(VehicleProperties.self)
        bus = try? busContainer.decode(BusProperties.self)
    }
}

let decoder = XMLDecoder()
decoder.keyDecodingStrategy = .convertFromCapitalized

let properties1 = try decoder.decode(AllProperties.self, from: vehicle1)
let properties2 = try decoder.decode(AllProperties.self, from: vehicle2)

MaxDesiatov avatar Apr 18 '19 18:04 MaxDesiatov

Hi @mhs2342, I would like to add more sample code to the README of XMLCoder, would you mind if I take your XML snippet and use it that way? Thank you!

MaxDesiatov avatar Apr 24 '19 14:04 MaxDesiatov

@MaxDesiatov Sure! Sorry for the delay, I haven't had a chance to dive into this yet. You can find the api where similar requests happen here

mhs2342 avatar Apr 24 '19 14:04 mhs2342

Hi @mhs2342! I know it's been a while, were you able to have a look and can the issue be resolved at this point?

MaxDesiatov avatar Oct 17 '19 12:10 MaxDesiatov