Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

Doubly nested arrays #37

Closed
kiliankoe opened this issue Feb 26, 2016 · 9 comments
Closed

Doubly nested arrays #37

kiliankoe opened this issue Feb 26, 2016 · 9 comments

Comments

@kiliankoe
Copy link

Hi, been waiting to use mapper for a personal project for quite a while now and finally came around to doing so. Really loving it!

The only problem is that the API I'm working with is sending data as a doubly-nested array at one point. And I'm not quite sure how to work with this 😕

I've defined my model struct like this:

struct Building {
    // ...
    let points: [[CLLocationCoordinate2D]]
}

Deserializing like this unfortunately isn't working:

extension Building: Mappable {
    init(map: Mapper) throws {
        // ...
        try points = map.from("punkte")
    }
}

Is there a simple way to get around this? Should this be working and I'm missing something else?

Thanks for any help in advance 😊

@keith
Copy link
Contributor

keith commented Feb 26, 2016

Hey @kiliankoe, thanks for reporting this. This isn't a case we've dealt with before. It's doubly interesting because if your type was able to be casted by swift (say [[String]]) this would work as is right now. But since you're using a Convertible this doesn't work out.

One solution to this would be:

let arrays: [NSArray] = try map.from("punkte")
points = try arrays.map { array in try array.map(CLLocationCoordinate2D.fromMap) }

Where the first step is getting the nested arrays from the JSON. Of course if you wanted to you could also make that more specific, such as [[[String: String]]] or whatever your actual type is.

Then the second step is mapping over this array of arrays, creating coordinates from your custom Convertible function.

This obviously isn't the prettiest solution, but I don't think that we currently want to add direct support to mapper for custom 2d arrays like this. Ideally in the future you could also make an extension on Array for a specific type while also conforming to a protocol. For example that might look like:

extension Array: Convertible where Element: Convertible

But right now having this type constraint while also conforming to a protocol seems to be impossible.

@kiliankoe
Copy link
Author

Fantastic, might be a workaround, but it's definitely reasonable and seems to work fine 😊 Thanks for the quick answer, I really appreciate it!

@esttorhe
Copy link

I tried this on Xcode 7.2.1 and I'm getting the following error

Extension of type 'Array' with constraints cannot have an inheritance clause

I went down a similar route because when I try to do this:

let images: Array<NSDictionary> = try map.from("pictures")

I keeps crashing with MappableError; when debugging the value from pictures is correct; its an array of dictionaries.

Changing the type to NSArray doesn't fix the issue either sadly :(

@esttorhe
Copy link

In case anyone faces the same issue I was facing; I ended up changing the try map.from part to map.optionalFrom and left everything else the same and it worked.

Even though the JSON always contains that array this was the only workaround; otherwise it crashed 💯 of the time

¯_(ツ)_/¯

@keith
Copy link
Contributor

keith commented Mar 29, 2016

@esttorhe would you mind providing a little more context so I can setup a test case for this and see what the issue is?

@esttorhe
Copy link

Sure thing.

Pretty much this was the JSON that was causing the issue

{
  "succeeded": true,
  "result": {
    ".expires": "4/6/2016 9:24:53 PM",
    ".issued": "3/22/2016 9:24:53 PM",
    "access_token": "12341234123412341234",
    "as:client_id": "appid",
    "expires_in": "1209600",
    "profile":     {
        "userName": "sample string 1",
        "firstName": "sample string 2",
        "lastName": "sample string 3",
        "age": 21,
        "minimumAgeInterest": 20,
        "maximumAgeInterest": 28,
        "sex": 0,
        "interest": 0,
        "school": "sample string 7",
        "work": "sample string 8",
        "latitude": 9.1,
        "longitude": 10.1,
        "distance": 11.1,
        "availablePoints": 12,
        "pictures": [
           {
             "id": 1,
             "picture": "sample string 2",
             "order": 3
           },
           {
             "id": 1,
             "picture": "sample string 2",
             "order": 3
           }
        ],
        "best": [ {
            "questionId": 1,
            "text": "sample string 2",
            "picture": "sample string 3",
            "answer": 0
          }, {
            "questionId": 1,
            "text": "sample string 2",
            "picture": "sample string 3",
            "answer": 0
          }
        ],
        "worst": [
                  {
                  "questionId": 1,
                  "text": "sample string 2",
                  "picture": "sample string 3",
                  "answer": 0
                  },
                  {
                  "questionId": 1,
                  "text": "sample string 2",
                  "picture": "sample string 3",
                  "answer": 0
                  }
                  ]
    },
    "refresh_token": "123412341234",
    "token_type": "bearer",
    "userName": "user@email.com",
  }
}

When decoding the pictures array I was assigning it to a var then doing some map and sort by decoding the pictures like this:

let images: Array<NSDictionary> = try map.from("pictures")

The above code inside the init(map: Mapper) of my struct. Which was causing the crash.

Changing that to something like this:

if let images: Array<NSDictionary> = try map.optionalFrom("pictures") {

Fixed the crash.

@keith
Copy link
Contributor

keith commented Apr 12, 2016

That's interesting. It seems like this test passes:

    func testNestedArrayOfDictionaries() {
        struct Test: Mappable {
            let array: [NSDictionary]
            init(map: Mapper) throws {
                try array = map.from("pictures")
            }
        }

        let object: NSDictionary = [
            "pictures": [
                [
                    "id": 1,
                    "picture": "sample string 2",
                    "order": 3
                ],
                [
                    "id": 1,
                    "picture": "sample string 2",
                    "order": 3
                ],
            ]
        ]

        let test = Test.from(object)
        XCTAssertTrue(test?.array.count == 2)
    }

@esttorhe
Copy link

Mmm; that's interesting.

Will try again later today and will update my findings.

Thanks for checking this BTW

@keith
Copy link
Contributor

keith commented Jul 13, 2016

Closing this, feel free to reopen/open a new issue if there's a new problem here!

@keith keith closed this as completed Jul 13, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants