Closed jlyonsmith closed 7 years ago
Here's a more complete example of the problem. Paste this into a playground with the Marshal sources:
public struct Tableau: Marshaling, Unmarshaling {
public var matrix: [[Int]]?
public init(object: MarshaledObject) throws {
self.matrix = try object.value(for: "matrix")
}
public init(matrix: [[Int]]?) {
self.matrix = matrix
}
public func marshaled() -> [String: Any] {
return [
"matrix": matrix ?? [],
]
}
}
Sorry. I read this the other day and then got pulled away while trying to figure out a good response and then the weekend happened.
This is a really good question: I'm not sure what the answer is here. It's not a case I've dealt with yet.
We have support for value(for:) -> [A]
on MarshaledObject
, and [ValueType]
conforms to Marshal.ValueType
. So in theory, I would expect an array of arrays of ValueType
to work, but apparently it doesn't. I'm going to file this as a bug.
We may need to add explicit support for [[A]]
on MarshaledObject
.
@jarsen An array doesn't conform to ValueType
which is why an array of arrays don't work. I think we could add support for it by extending MarshaledObject
with a version of value(for:) -> [A]
where A
doesn't conform to ValueType
and it would call a new value(from:)
method on Array
where Element
doesn't conform to ValueType
.
AH. Yes you're right. I misread the extension Array where Element: ValueType
😝
That could work.
Hi, I'm trying to deserialize like so:
let object = try! JSONParser.JSONObjectWithData(jsonData)
let array: [[String: Any]] = try object.value(for: "data.arrayOfDictionaries")
I'd really love to be able to do this! I feel like this basically the same issue as described above.
I was looking into this to see how #86 impacted this, it now won't compile instead of not working.
let names: [[String]] = try! json.value(for: "names")
I think this is better behavior, and should just need more methods to support all of the possibilities. I want to check in here before I add all of these possibilities though. It will almost double the amount of methods in here to fully support nested arrays, which is a bit painful. Array
and Dictionary
are ballooning the number of value(for:)
functions a lot. I believe this will clean up nicely when Swift 4 lands with constrained protocol conformances. It may make more sense to not support nested arrays until Swift gets constrained protocol conformances.
I'm of the mind to not add explicit support, and leave that as an exercise to those who need it. I'd rather keep things simple, especially if they're more edge case.
Given our discussion up to this point, I'm going to suggest that Marshal itself will not add the functionality necessary for arrays of arrays. I am willing to revisit the topic if someone comes with a compelling proposal/PR. Thanks!
@Shmaff I believe with some of the latest changes that unmarshaling [MarshaledObject]
should work. I think it is a different issue though from the nested arrays. If you would like to verify, that would be great! If not feel free to open up a new issue :)
I'm facing almost the same issue of having to parse an array of arrays of arrays \o/.
"boundaries": [
[
[
0.148271,
51.6723432
],
[
0.148271,
51.3849401
],
[
-0.3514683,
51.3849401
],
[
-0.3514683,
51.6723432
],
[
0.148271,
51.6723432
]
]
]
Since the last nested array level is coordinates, I've figured out that I could make CLLocationCoordinate2D
conforms to ValueType
.
extension CLLocationCoordinate2D: ValueType {
public static func value(from object: Any) throws -> CLLocationCoordinate2D {
guard let array = object as? [Double] else {
throw MarshalError.typeMismatch(expected: [Double].self, actual: type(of: object))
}
guard array.count == 2 else {
throw MarshalError.typeMismatch(expected: "Array expected to contains 2 values", actual: "Array contains \(array.count) value(s)")
}
return CLLocationCoordinate2D(latitude: array[0], longitude: array[1])
}
}
However I couldn't figure out how to parse the array of arrays. This trigger a compile error as expected by reading this comment:
let coordinates: [[CLLocationCoordinate2D]] = try object.value(for: "boundaries")
I'm considering switching to Marshal but this is a key feature for me.
Of course I can't change the JSON format, that would be too easy...
+1 for adding multi-dimensional array support to Marshal.
@amarcadet: I currently need to parse similar data (simple polygons from a GEOJson-compatible format), and use this:
let coords = try object.any(for: "coordinates")
if let coordinates = coords as? [[[Double]]] {
self.coordinates = coordinates[0].map { CLLocationCoordinate2D(latitude: $0[0], longitude: $0[1]) } }
else {
throw MarshalError.typeMismatchWithKey(key: "coordinates", expected: "[[[Double]]]", actual: coords)
}
Do the Swift 3.1 generic constraint additions make it any more possible to support this? I had a quick look, but couldn't make it work.
So is there any ugly workaround at least ? I am using Marshal and I love it , but how should I parse 2 dimensional arrays ?
I do it like this:
let anyObject: Any = try object.any(for: "json_key")
let matrix = handle(anyObject)
func handle(_ object: Any) -> [[CustomStruct]] {
var map: [[CustomStruct]] = []
if let array = object as? [Any] {
for subArrayItem in array {
if let subArray = subArrayItem as? [Any] {
var row: [CustomStruct] = []
for item in subArray {
if let json = item as? [String: Any] {
if let brick = try? CustomStruct(object: json) {
row.append(brick)
}
}
}
map.append(row)
}
}
}
return map
}
I'm trying to marshal a property like:
Where
Thing
is aUnmarshaling
. But I get aNo 'value' candidates produce the expected contextual result type '[[Thing]]?'
. I tried defining extensions toMarshableObject.value
that return[[A]]
but got stuck. It seems like forArray<Array<A>>
Swift doesn't recognize the innerArray<A>
type as aValueType
?