[swift-users] dealing with heterogenous lists/dictionary with Codable
Geordie Jay
geojay at gmail.com
Thu Oct 19 14:24:44 CDT 2017
David Baraff <davidbaraff at gmail.com> schrieb am Do. 19. Okt. 2017 um 21:14:
> 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.
>
This is already possible, just not with dictionaries of unknown types
(because they’re not known to be Codable)
> So basically, back to wanting to let the compiler do the work, when I make
> new structures, while still allowing for heterogenous containers.
>
It’s also possible to give the compiler hints as to what decodes into what.
Have you looked at the docs on the Apple foundation page?
https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types
Geordie
>
>
>
>
> 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>
>
> 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>
>
> Cc: Geordie Jay <geojay at gmail.com>, swift-users <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> 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> 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> wrote:
>>
>>
>> David Baraff via swift-users <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
>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>
>> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> 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/0af05b05/attachment.html>
More information about the swift-users
mailing list