[swift-users] dealing with heterogenous lists/dictionary with Codable
Itai Ferber
iferber at apple.com
Thu Oct 19 14:48:18 CDT 2017
David,
Is there an issue with extending the enum as necessary with new cases to
support what you need?
```swift
enum MyType : Codable {
case int(Int)
case string(String)
case newThingy(NewThingy)
case list([MyType])
case dictionary([String : MyType])
// …
}
```
`NewThingy` can still be declared on its own and have a synthesized
`Codable` implementation.
— Itai
On 19 Oct 2017, at 12:14, David Baraff wrote:
> My apologies. I misstated the problem: I don’t want to just limit
> to Int, String, [Int], etc. but also allow structures where
>
> struct NewThingy : Codable {
> let data1: T1
> let data2: T2
> }
>
> where T1 and T2 are themselves Codable.
>
> So basically, back to wanting to let the compiler do the work, when I
> make new structures, while still allowing for heterogenous containers.
>
>
>
>
>
> Begin forwarded message:
>
>> From: Itai Ferber <iferber at apple.com>
>> Subject: Re: [swift-users] dealing with heterogenous lists/dictionary
>> with Codable
>> Date: October 19, 2017 at 10:40:28 AM PDT
>> To: David Baraff <davidbaraff at gmail.com>
>> Cc: Geordie Jay <geojay at gmail.com>, swift-users
>> <swift-users at swift.org>
>>
>> Why are you stuck? I think the following matches your needs, no?
>>
>> import Foundation
>>
>> enum MyType : Codable, Equatable {
>> case int(Int)
>> case string(String)
>> case list([MyType])
>> case dictionary([String : MyType])
>>
>> public init(from decoder: Decoder) throws {
>> // Can be made prettier, but as a simple example:
>> let container = try decoder.singleValueContainer()
>> do {
>> self = .int(try container.decode(Int.self))
>> } catch DecodingError.typeMismatch {
>> do {
>> self = .string(try container.decode(String.self))
>> } catch DecodingError.typeMismatch {
>> do {
>> self = .list(try container.decode([MyType].self))
>> } catch DecodingError.typeMismatch {
>> self = .dictionary(try container.decode([String :
>> MyType].self))
>> }
>> }
>> }
>> }
>>
>> public func encode(to encoder: Encoder) throws {
>> var container = encoder.singleValueContainer()
>> switch self {
>> case .int(let int): try container.encode(int)
>> case .string(let string): try container.encode(string)
>> case .list(let list): try container.encode(list)
>> case .dictionary(let dictionary): try
>> container.encode(dictionary)
>> }
>> }
>>
>> static func ==(_ lhs: MyType, _ rhs: MyType) -> Bool {
>> switch (lhs, rhs) {
>> case (.int(let int1), .int(let int2)): return int1 == int2
>> case (.string(let string1), .string(let string2)): return
>> string1 == string2
>> case (.list(let list1), .list(let list2)): return list1 ==
>> list2
>> case (.dictionary(let dict1), .dictionary(let dict2)): return
>> dict1 == dict2
>> default: return false
>> }
>> }
>> }
>>
>> let values: MyType = .list([.int(42), .string("hello!"),
>> .list([.int(9), .string("hi")]), .dictionary(["zero": .int(0), "one":
>> .int(1)])])
>> print(values)
>>
>> let encoder = JSONEncoder()
>> let data = try encoder.encode(values)
>> print(String(data: data, encoding: .utf8)!) // =>
>> [42,"hello!",[9,"hi"],{"zero":0,"one":1}]
>>
>> let decoder = JSONDecoder()
>> let decoded = try decoder.decode(MyType.self, from: data)
>> print(decoded)
>>
>> print(values == decoded) // => true
>> On 19 Oct 2017, at 20:15, David Baraff wrote:
>>
>> Begin forwarded message:
>>
>>> From: Itai Ferber <iferber at apple.com <mailto:iferber at apple.com>>
>>> Subject: Re: [swift-users] dealing with heterogenous
>>> lists/dictionary with Codable
>>> Date: October 19, 2017 at 9:39:25 AM PDT
>>> To: David Baraff <davidbaraff at gmail.com
>>> <mailto:davidbaraff at gmail.com>>
>>> Cc: Geordie Jay <geojay at gmail.com <mailto:geojay at gmail.com>>,
>>> swift-users <swift-users at swift.org <mailto:swift-users at swift.org>>
>>>
>>> Hi David and Geordie,
>>>
>>> That approach won’t work — encoders and decoders only work
>>> directly with concrete Codable types (e.g. String, Int, MyFoo [where
>>> MyFoo is Codable], etc.).
>>> This is by design: since there is no type information stored in the
>>> JSON payload, there isn’t necessarily a way to tell how to decode
>>> the type you’re looking at, so asking for a generalCodable`
>>> isn’t helpful.
>>>
>>> Since it’s unlikely that what you truly need is a [String : Any]
>>> but really a [String : <one of String, Int, MyFoo, etc.>], one easy
>>> way to decode this type is to create a wrapper enum or similar which
>>> overrides init(from:) to be able to decode from one of those types.
>>> You can then ask to decode a [String : MyWrapperType] and use that
>>> instead.
>>>
>>> What types are you expecting in the dictionary?
>>>
>>>
>>
>> The problem is that I want to be able to encode types T where
>> (a) T is String, Int
>> (b) lists of T
>> (c ) dictionaries of type <String, T>
>>
>> The problem is the recursive nature: yes, my types are simple (say
>> only base types String and Int) but the “nesting” level may be
>> quite deep (a list of list of dictionaries of <etc.).
>>
>>
>> Let’s turn this around: in addition to the JSONEncoder, one can
>> also use the PropertyListEncoder.
>>
>> Are we saying that something one could pull from a property list file
>> (which is pretty much what i want: arbitrary deep nesting of basic
>> types) is also not Codable? So a PropertyListEncoder could not
>> encode actual property lists?
>>
>> I really do want a heterogenous container. I think I am stuck.
>>
>>> — Itai
>>>
>>> On 19 Oct 2017, at 18:11, David Baraff via swift-users wrote:
>>>
>>> I’ll try. Is that cast smart enough to apply recursively? We
>>> shall see.
>>>
>>> Sent from my iPad
>>>
>>> On Oct 19, 2017, at 7:34 AM, Geordie Jay <geojay at gmail.com
>>> <mailto:geojay at gmail.com>> wrote:
>>>
>>>> I mean can you do something along the lines of
>>>>
>>>> let codableDict = stringAnyDict as? [String : Codable]
>>>>
>>>> ?
>>>>
>>>> I’m not at a computer to test it myself
>>>>
>>>>
>>>>
>>>>
>>>> David Baraff <davidbaraff at gmail.com <mailto:davidbaraff at gmail.com>>
>>>> schrieb am Do. 19. Okt. 2017 um 15:45:
>>>> That’s exactly what I want. The ironic part is that I got my
>>>> dictionary by decoding a Json file. If that’s where my
>>>> dictionary came from, is there a simple way of coercing the Json
>>>> serialization routines to give me back codables, rather than Anys?
>>>>
>>>>
>>>> Sent from my iPad
>>>>
>>>> On Oct 19, 2017, at 3:38 AM, Geordie Jay <geojay at gmail.com
>>>> <mailto:geojay at gmail.com>> wrote:
>>>>
>>>>>
>>>>> David Baraff via swift-users <swift-users at swift.org
>>>>> <mailto:swift-users at swift.org>> schrieb am Do. 19. Okt. 2017 um
>>>>> 03:47:
>>>>> So I have simple structs like this:
>>>>>
>>>>> struct Library: Codable {
>>>>> let domain: String
>>>>> let unit: String
>>>>> }
>>>>>
>>>>> and it’s super-simple to serialize. Yay.
>>>>>
>>>>> But:
>>>>>
>>>>> struct LibraryGroup : Codable { // I wish...
>>>>> let libraries: [Library]
>>>>> let someDict: [String : Any]
>>>>> }
>>>>>
>>>>> I haven’t tried this, but is it possible to have a dictionary of
>>>>> [String : Codable] ? Because that’s exactly the type
>>>>> requirements you’re describing, no?
>>>>>
>>>>> Geordie
>>>>>
>>>>>
>>>>> So what I’m looking for is something where if the values in
>>>>> someDict are themselves Codable, I can serialize things, and if
>>>>> they’re not, I can’t. In my previous scheme, I was using
>>>>> NSKeyedArchiver to serialize everything, manualy, including
>>>>> someDict; in trying to switch to Codable I ran smack into the fact
>>>>> that Codable wants to know what all the types are, in advance.
>>>>>
>>>>> Am I just stuck? How do I get the best of both worlds, where the
>>>>> compiler can make use of the fact that it can see the data types
>>>>> of my structures, while still being able to serialize heterogenous
>>>>> data like is found in LibraryGroup?
>>>>>
>>>>> Is my only alternative to write a custom coder for LibraryGroup?
>>>>> Is there any hope I could teach Codable what to do with
>>>>> [String: Any]
>>>>>
>>>>> ?
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> swift-users mailing list
>>>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>> <https://lists.swift.org/mailman/listinfo/swift-users>
>>>
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org <mailto:swift-users at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-users
>>> <https://lists.swift.org/mailman/listinfo/swift-users>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171019/5f452b1d/attachment.html>
More information about the swift-users
mailing list