Decode a JSON object of unknown format into a Dictionary with Decodable in Swift 4

Say I have a JSON object such as:

  {
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "name":"John Doe",
    "metadata": {
      "link_id": "linked-id",
      "buy_count": 4
    }
  }

And with a struct of:

struct User: Codable {
  var id: String
  var email: String
  var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
  var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

Your issue here is Any, which will likely never be Decodable. You’ll need an actual type to contain the raw JSON. Hilariously, I have to wonder if Argo’s JSON enum could be made Decodable, as it can represent every valid JSON type typically contained in the Any returned by JSONSerialization. Yet another reason why Swift needs a real JSON serialization.

Jon Shier

···

On Jun 17, 2017, at 10:07 PM, Chris Anderson via swift-users <swift-users@swift.org> wrote:

Say I have a JSON object such as:

  {
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com <mailto:john.doe@example.com>",
    "name":"John Doe",
    "metadata": {
      "link_id": "linked-id",
      "buy_count": 4
    }
  }

And with a struct of:

struct User: Codable {
  var id: String
  var email: String
  var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
  var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

···

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org> wrote:

Say I have a JSON object such as:

  {
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "name":"John Doe",
    "metadata": {
      "link_id": "linked-id",
      "buy_count": 4
    }
  }

And with a struct of:

struct User: Codable {
  var id: String
  var email: String
  var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
  var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

···

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org> wrote:

Say I have a JSON object such as:

{
   "id": "4yq6txdpfadhbaqnwp3",
   "email": "john.doe@example.com",
   "name":"John Doe",
   "metadata": {
     "link_id": "linked-id",
     "buy_count": 4
   }
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Your gist is extremely interesting to me. I had tried something similar with Argo’s existing JSON enum, but was somewhat stymied by trying to decode it from an unkeyed container. I suppose I still don’t have a good grasp on all of the existing container types.

Jon

···

On Jun 23, 2017, at 11:46 AM, Randy Eckenrode via swift-users <swift-users@swift.org> wrote:

On Jun 17, 2017, at 10:07 PM, Chris Anderson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Say I have a JSON object such as:

  {
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com <mailto:john.doe@example.com>",
    "name":"John Doe",
    "metadata": {
      "link_id": "linked-id",
      "buy_count": 4
    }
  }

And with a struct of:

struct User: Codable {
  var id: String
  var email: String
  var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
  var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

It’s possible, but you have to do most of the work yourself because you the compiler can’t create implementations for you. See below for a possible implementation. Note that I just ignore types I don’t handle. I also took a stab at doing something general in this gist (https://gist.github.com/kenada/069e121371eb8db41231edfcd4bd14a8), but it doesn’t implement very robust error handling or support encoding. It also doesn’t flatten down to Any/[Any]/[String: Any] (leaving it up to the user to destructure the enum).

import Foundation

struct User: Codable {
    var id: String
    var email: String
    var name: String
    var metadata: [String: Any]

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: StaticCodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.email = try container.decode(String.self, forKey: .email)
        self.name = try container.decode(String.self, forKey: .name)
        self.metadata = try User.decodeMetadata(from: container.superDecoder(forKey: .metadata))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: StaticCodingKeys.self)
        try container.encode(self.id, forKey: .id)
        try container.encode(self.email, forKey: .email)
        try container.encode(self.name, forKey: .name)
        try encodeMetadata(to: container.superEncoder(forKey: .metadata))
    }

   static func decodeMetadata(from decoder: Decoder) throws -> [String: Any] {
        let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
        var result: [String: Any] = [:]
        for key in container.allKeys {
            if let double = try? container.decode(Double.self, forKey: key) {
                result[key.stringValue] = double
            } else if let string = try? container.decode(String.self, forKey: key) {
                result[key.stringValue] = string
            }
        }
        return result
    }

    func encodeMetadata(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: DynamicCodingKeys.self)
        for (key, value) in metadata {
            switch value {
            case let double as Double:
                try container.encode(double, forKey: DynamicCodingKeys(stringValue: key)!)
            case let string as String:
                try container.encode(string, forKey: DynamicCodingKeys(stringValue: key)!)
            default:
                fatalError("unexpected type")
            }
        }
    }

    private enum StaticCodingKeys: String, CodingKey {
        case id, email, name, metadata
    }

    private struct DynamicCodingKeys: CodingKey {
        var stringValue: String

        init?(stringValue: String) {
            self.stringValue = stringValue
        }

        var intValue: Int?

        init?(intValue: Int) {
            self.init(stringValue: "")
            self.intValue = intValue
        }
    }
}

let userJson = """
  {
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com <mailto:john.doe@example.com>",
    "name":"John Doe",
    "metadata": {
      "link_id": "linked-id",
      "buy_count": 4
    }
  }
""".data(using: .utf8)!

let decoder = JSONDecoder()
let user = try! decoder.decode(User.self, from: userJson)
print(user)
// Prints: User(id: "4yq6txdpfadhbaqnwp3", email: "john.doe@example.com <mailto:john.doe@example.com>", name: "John Doe", metadata: ["buy_count": 4.0, "link_id": "linked-id"])

let encoder = JSONEncoder()
let data = try! encoder.encode(user)
print(String(data: data, encoding: .utf8)!)
// Prints: {"email":"john.doe@example.com <mailto:john.doe@example.com>","id":"4yq6txdpfadhbaqnwp3","metadata":{"link_id":"linked-id","buy_count":4},"name":"John Doe"}

--
Randy Eckenrode
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

···

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org> wrote:

Say I have a JSON object such as:

{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Create a struct for Metadata and conform to Coding

Code:

let string = """
{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}
"""

let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead

struct User: Codable {
    
    //Struct to handle the nested JSON
    struct Metadata : Codable {
        var linkID : String
        var buyCount : Int
        
        //Customisation, if you want if you want your properties to be different from the JSON key
        private enum CodingKeys : String, CodingKey {
            case linkID = "link_id"
            case buyCount = "buy_count"
        }
    }
    
    var id: String
    var email : String
    var name : String
    var metadata : Metadata
}

let decoder = JSONDecoder()

do {
    let user = try decoder.decode(User.self, from: data)
    print(user)
}
catch {
    print("error:\(error)")
}

Reference:

https://developer.apple.com/videos/play/wwdc2017/212/

Regards,
Muthu

···

On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users@swift.org> wrote:

  The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org> wrote:

Say I have a JSON object such as:

{
"id": "4yq6txdpfadhbaqnwp3",
"email": "john.doe@example.com",
"name":"John Doe",
"metadata": {
   "link_id": "linked-id",
   "buy_count": 4
}
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

1 Like

Given that, per his description, “metadata” can be anything, creating a struct doesn’t really help.

Jon Shier

···

On Jun 18, 2017, at 9:00 PM, somu subscribe <somu.subscribe@gmail.com> wrote:

Create a struct for Metadata and conform to Coding

Code:

let string = """
{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com <mailto:john.doe@example.com>",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}
"""

let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead

struct User: Codable {
    
    //Struct to handle the nested JSON
    struct Metadata : Codable {
        var linkID : String
        var buyCount : Int
        
        //Customisation, if you want if you want your properties to be different from the JSON key
        private enum CodingKeys : String, CodingKey {
            case linkID = "link_id"
            case buyCount = "buy_count"
        }
    }
    
    var id: String
    var email : String
    var name : String
    var metadata : Metadata
}

let decoder = JSONDecoder()

do {
    let user = try decoder.decode(User.self, from: data)
    print(user)
}
catch {
    print("error:\(error)")
}

Reference:

https://developer.apple.com/videos/play/wwdc2017/212/

Regards,
Muthu

On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl <mailto:Rien@Balancingrock.nl>> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Say I have a JSON object such as:

{
"id": "4yq6txdpfadhbaqnwp3",
"email": "john.doe@example.com <mailto:john.doe@example.com>",
"name":"John Doe",
"metadata": {
   "link_id": "linked-id",
   "buy_count": 4
}
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

Hi Jon,

Usually this boils down to a question of: what are you going to do with the Any?

If you intended to cast it to a dictionary and get at its values using string keys, then writing a struct with the properties you care about is the way we recommend doing this. You don’t have to include every possible key.

There are some more dynamic behaviors possible with the Any, but the point of this API was to try to move a lot of the parsing into a place where the compiler could help you not only to generate the boilerplate but catch errors at compile time.

The omission of an API to just get the rest of the data as some kind of Any is both for reasons of abstraction (not every data format is JSON, and the Any truly could be anything in other formats), and a statement of intent of how we believe this API is best used.

- Tony

···

On Jun 18, 2017, at 7:23 PM, Jon Shier via swift-users <swift-users@swift.org> wrote:

Given that, per his description, “metadata” can be anything, creating a struct doesn’t really help.

Jon Shier

On Jun 18, 2017, at 9:00 PM, somu subscribe <somu.subscribe@gmail.com <mailto:somu.subscribe@gmail.com>> wrote:

Create a struct for Metadata and conform to Coding

Code:

let string = """
{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com <mailto:john.doe@example.com>",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}
"""

let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead

struct User: Codable {
    
    //Struct to handle the nested JSON
    struct Metadata : Codable {
        var linkID : String
        var buyCount : Int
        
        //Customisation, if you want if you want your properties to be different from the JSON key
        private enum CodingKeys : String, CodingKey {
            case linkID = "link_id"
            case buyCount = "buy_count"
        }
    }
    
    var id: String
    var email : String
    var name : String
    var metadata : Metadata
}

let decoder = JSONDecoder()

do {
    let user = try decoder.decode(User.self, from: data)
    print(user)
}
catch {
    print("error:\(error)")
}

Reference:

https://developer.apple.com/videos/play/wwdc2017/212/

Regards,
Muthu

On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl <mailto:Rien@Balancingrock.nl>> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Say I have a JSON object such as:

{
"id": "4yq6txdpfadhbaqnwp3",
"email": "john.doe@example.com <mailto:john.doe@example.com>",
"name":"John Doe",
"metadata": {
   "link_id": "linked-id",
   "buy_count": 4
}
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Tony:
  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t. In fact, it can’t. If that’s considered okay by the designers of the library and Apple itself, then fine. I think it’s silly (just like I think it’s silly that, after years of waiting, NSISO8601DateFormatter doesn’t actually support the full ISO 8601 standard) and does a disservice to developers, but fine. For example, many JSON APIs (from the very worst to some of the best) use a common reply structure:

{
  “value” : { } or null
  “error” : { } or null
}

Having implemented a few of these using Argo, I usually follow a common pattern:

struct APIResponse: Argo.Decodable {
  let value: JSON?
  let error: APIError?
}

I then decode the response, check to see which of the parts is non-null, and continue decoding based on that result. I decode the value based on the generic type passed in, usually in an Alamofire response serializer. Codable can sort of represent this, with this:

struct APIResponse<T: Decodable>: Decodable {
    let value: T?
    let error: String? // String as I haven’t defined an error type.
}

  This works, AFAICT, but loses the rather important ability to inspect the decoding result before attempting to decode the generic value. Codable would be perfectly happy to return me an APIResponse with two nil values, which is an invalid state for this API and should be an error. (As an aside, defaulting all errors for optional values to nil is poor practice and we lose some error fidelity there.) This is really just a symptom of Foundation not having a real JSON limitation. But it’s doubly concerning that the JSON representation it does have, Any, can’t be parsed by it’s own JSON decoder.
  Dealing with data also limits Codable’s use for JSON to only things that have data representation. For example, push notifications can contain custom JSON payloads. On Apple platforms the push handler automatically decodes this JSON before passing the result to the delegates in the app. In the past I’ve been able to use the same JSON representations to parse the Any returned by the push delegates as I do in my networking API, which gives me exactly the same parsing for both methods. Codable can’t be used here.
  In addition, Codable’s cumbersome API for custom APIs is rather painful to deal with, and the official recommended solution to use *Raw types that come from your API and are used to initialize your “real” types is untenable (on one project, every type I got in a response wouldn’t have required this, forcing me to maintain 60 types, manually). Add to that the manual nature of any sort of transform or validation, and Codable for real JSON decoding becomes rather painful.
  For me, this means that I’ll likely stick to libraries like Argo for actual JSON decoding (greater fidelity and flexibility in far less code) and use Codable for disk and other transfer representations (I look forward to writing my watch communication using it) only, in all but the most ideal circumstances (if I design the JSON API I can optimize it around Codable).

Jon Shier

···

On Jun 22, 2017, at 7:10 PM, Tony Parker <anthony.parker@apple.com> wrote:

Hi Jon,

Usually this boils down to a question of: what are you going to do with the Any?

If you intended to cast it to a dictionary and get at its values using string keys, then writing a struct with the properties you care about is the way we recommend doing this. You don’t have to include every possible key.

There are some more dynamic behaviors possible with the Any, but the point of this API was to try to move a lot of the parsing into a place where the compiler could help you not only to generate the boilerplate but catch errors at compile time.

The omission of an API to just get the rest of the data as some kind of Any is both for reasons of abstraction (not every data format is JSON, and the Any truly could be anything in other formats), and a statement of intent of how we believe this API is best used.

- Tony

On Jun 18, 2017, at 7:23 PM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Given that, per his description, “metadata” can be anything, creating a struct doesn’t really help.

Jon Shier

On Jun 18, 2017, at 9:00 PM, somu subscribe <somu.subscribe@gmail.com <mailto:somu.subscribe@gmail.com>> wrote:

Create a struct for Metadata and conform to Coding

Code:

let string = """
{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com <mailto:john.doe@example.com>",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}
"""

let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead

struct User: Codable {
    
    //Struct to handle the nested JSON
    struct Metadata : Codable {
        var linkID : String
        var buyCount : Int
        
        //Customisation, if you want if you want your properties to be different from the JSON key
        private enum CodingKeys : String, CodingKey {
            case linkID = "link_id"
            case buyCount = "buy_count"
        }
    }
    
    var id: String
    var email : String
    var name : String
    var metadata : Metadata
}

let decoder = JSONDecoder()

do {
    let user = try decoder.decode(User.self, from: data)
    print(user)
}
catch {
    print("error:\(error)")
}

Reference:

https://developer.apple.com/videos/play/wwdc2017/212/

Regards,
Muthu

On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl <mailto:Rien@Balancingrock.nl>> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Say I have a JSON object such as:

{
"id": "4yq6txdpfadhbaqnwp3",
"email": "john.doe@example.com <mailto:john.doe@example.com>",
"name":"John Doe",
"metadata": {
   "link_id": "linked-id",
   "buy_count": 4
}
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

Hi Jon,

Tony:
  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t. In fact, it can’t. If that’s considered okay by the designers of the library and Apple itself, then fine. I think it’s silly (just like I think it’s silly that, after years of waiting, NSISO8601DateFormatter doesn’t actually support the full ISO 8601 standard) and does a disservice to developers, but fine. For example, many JSON APIs (from the very worst to some of the best) use a common reply structure:

{
  “value” : { } or null
  “error” : { } or null
}

Having implemented a few of these using Argo, I usually follow a common pattern:

struct APIResponse: Argo.Decodable {
  let value: JSON?
  let error: APIError?
}

I then decode the response, check to see which of the parts is non-null, and continue decoding based on that result. I decode the value based on the generic type passed in, usually in an Alamofire response serializer. Codable can sort of represent this, with this:

struct APIResponse<T: Decodable>: Decodable {
    let value: T?
    let error: String? // String as I haven’t defined an error type.
}

  This works, AFAICT, but loses the rather important ability to inspect the decoding result before attempting to decode the generic value. Codable would be perfectly happy to return me an APIResponse with two nil values, which is an invalid state for this API and should be an error. (As an aside, defaulting all errors for optional values to nil is poor practice and we lose some error fidelity there.) This is really just a symptom of Foundation not having a real JSON limitation. But it’s doubly concerning that the JSON representation it does have, Any, can’t be parsed by it’s own JSON decoder.

Have you considered using an enum?

let jsonA = """
{
"key1" : 1
}
""".data(using: .utf8)!

let jsonB = """
{
"key2" : "foo"
}
""".data(using: .utf8)!

enum EitherOr : Decodable {
    case A(Int)
    case B(String)
    
    private enum CodingKeys : CodingKey { case key1; case key2 }
    
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        if c.contains(.key1) {
            self = .A(try c.decode(Int.self, forKey: .key1))
        } else if c.contains(.key2) {
            self = .B(try c.decode(String.self, forKey: .key2))
        } else {
            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "”))
        }
    }
}

let decoder = JSONDecoder()
let result1 = try! decoder.decode(EitherOr.self, from: jsonA)
print(result1)
let result2 = try! decoder.decode(EitherOr.self, from: jsonB)
print(result2)

A(1)
B("foo")

  Dealing with data also limits Codable’s use for JSON to only things that have data representation. For example, push notifications can contain custom JSON payloads. On Apple platforms the push handler automatically decodes this JSON before passing the result to the delegates in the app. In the past I’ve been able to use the same JSON representations to parse the Any returned by the push delegates as I do in my networking API, which gives me exactly the same parsing for both methods. Codable can’t be used here.
  In addition, Codable’s cumbersome API for custom APIs is rather painful to deal with, and the official recommended solution to use *Raw types that come from your API and are used to initialize your “real” types is untenable (on one project, every type I got in a response wouldn’t have required this, forcing me to maintain 60 types, manually). Add to that the manual nature of any sort of transform or validation, and Codable for real JSON decoding becomes rather painful.
  For me, this means that I’ll likely stick to libraries like Argo for actual JSON decoding (greater fidelity and flexibility in far less code) and use Codable for disk and other transfer representations (I look forward to writing my watch communication using it) only, in all but the most ideal circumstances (if I design the JSON API I can optimize it around Codable).

Jon Shier

Sure, there is only so much we can do completely automatically. One of our goals was to make the simplest stuff possible with as little boilerplate as possible, but provide the ability to customize when you want to do something more advanced (as above).

- Tony

···

On Jun 22, 2017, at 6:00 PM, Jon Shier <jon@jonshier.com> wrote:

On Jun 22, 2017, at 7:10 PM, Tony Parker <anthony.parker@apple.com <mailto:anthony.parker@apple.com>> wrote:

Hi Jon,

Usually this boils down to a question of: what are you going to do with the Any?

If you intended to cast it to a dictionary and get at its values using string keys, then writing a struct with the properties you care about is the way we recommend doing this. You don’t have to include every possible key.

There are some more dynamic behaviors possible with the Any, but the point of this API was to try to move a lot of the parsing into a place where the compiler could help you not only to generate the boilerplate but catch errors at compile time.

The omission of an API to just get the rest of the data as some kind of Any is both for reasons of abstraction (not every data format is JSON, and the Any truly could be anything in other formats), and a statement of intent of how we believe this API is best used.

- Tony

On Jun 18, 2017, at 7:23 PM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Given that, per his description, “metadata” can be anything, creating a struct doesn’t really help.

Jon Shier

On Jun 18, 2017, at 9:00 PM, somu subscribe <somu.subscribe@gmail.com <mailto:somu.subscribe@gmail.com>> wrote:

Create a struct for Metadata and conform to Coding

Code:

let string = """
{
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com <mailto:john.doe@example.com>",
  "name":"John Doe",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}
"""

let data = string.data(using: .utf8)! //Force unwrapping just for ease of explanation, use guard instead

struct User: Codable {
    
    //Struct to handle the nested JSON
    struct Metadata : Codable {
        var linkID : String
        var buyCount : Int
        
        //Customisation, if you want if you want your properties to be different from the JSON key
        private enum CodingKeys : String, CodingKey {
            case linkID = "link_id"
            case buyCount = "buy_count"
        }
    }
    
    var id: String
    var email : String
    var name : String
    var metadata : Metadata
}

let decoder = JSONDecoder()

do {
    let user = try decoder.decode(User.self, from: data)
    print(user)
}
catch {
    print("error:\(error)")
}

Reference:

https://developer.apple.com/videos/play/wwdc2017/212/

Regards,
Muthu

On 19 Jun 2017, at 3:29 AM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  The more I use Codable, the less suited to networking it becomes. In reading a variety of blog posts about implementing custom Decodable support from JSON, I kept running across the same pattern. Basically, users had started implementing their own decoding protocols which wrap Decodable types, and have a type that represents the JSON representation and then their real type, with an initializer connecting the two. But apparently this is Apple’s official solution, which is rather terrible since it would be completely unnecessary if the Decodable APIs were more flexible or we could access keys by key path rather than nesting full containers. I can’t image how much code I would have to add to decode the nasty JSON APIs I’ve used Argo to parse before. Every type would need an underlying raw representation that, at the very least, would need custom keys, lest I pollute even those models with the terrible keys the JSON actually has. Not to mention the various transforms I needed. Once I hit any reasonably complex API, Argo is far far simpler to implement in fewer lines of code.
  In trying to make Argo’s JSON enum Decodable itself, I can’t seem to find a way to access the Any representation of the raw JSON. In fact, it appears there’s no way to represent an Any value in Codable at all, which makes Codable rather useless for the scenarios like the one that prompted this thread. Without such an ability it’s impossible to actually use Codable with all of the JSON out there, where other solutions work just fine. Argo’s JSON type is decodable by Argo, so you can use it to represent a blob of JSON just fine. Other existing JSON frameworks have similar solutions.

Jon

On Jun 18, 2017, at 3:21 AM, Rien via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Dang, hit send too soon. Sorry.

This does not address your question, so please ignore… (foot in mouth)!

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 09:19, Rien <Rien@Balancingrock.nl <mailto:Rien@Balancingrock.nl>> wrote:

Are you looking for a general purpose JSON interpreter / API ?

There are many of them around, and in fact I do have my own: https://github.com/Balancingrock/VJson

With VJson I would write:

let json = VJson.parse(… your json object…)

and then access the metadata as:

let buyCount = (json | ”metadata” | ”buy_count”)?.intValue

or:

var buyCount: Int &= json | “metadata” | “buy_count”

To loop over the content of metadata:

for item in (json | “metadata”) ?? [ ] {
  print (item.nameValue)
  switch item.type {
  case .object: …
  case .number: …
  case .string: …
  etc...
  }
}

Obviously I am plugging my own code here, but there are many others around, I understand that SwiftyJSON is quite popular but I have not used that myself.

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/> - An HTTP(S) web server framework in Swift

On 18 Jun 2017, at 04:07, Chris Anderson via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Say I have a JSON object such as:

{
"id": "4yq6txdpfadhbaqnwp3",
"email": "john.doe@example.com <mailto:john.doe@example.com>",
"name":"John Doe",
"metadata": {
   "link_id": "linked-id",
   "buy_count": 4
}
}

And with a struct of:

struct User: Codable {
var id: String
var email: String
var name: String
}

How can I decode the `metadata` field into a Dictionary?

I’ve tried doing things such as, in my struct,

var metadata: Dictionary

or

var metadata: [String: Any]

But I get the error

MyPlayground.playground:3:7: note: cannot automatically synthesize 'Encodable' because '<<error type>>' does not conform to 'Encodable'
var metadata: Dictionary

A meta or metadata field on many APIs (such as www.stripe.com <http://www.stripe.com/>) can contain whatever you want, and I still want to be able to process it on the Swift end. How can I store that meta data field into a Dictionary that I can parse apart manually after?

Thanks!

Chris Anderson

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

···

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users <swift-users@swift.org> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

2 Likes

I’m sorry, are you complaining about my use of Codable instead of more precisely referring to the JSON endcode/decode functionality based on it in Foundation, or are you honestly trying to say that said functionality was never intended to be a general purpose JSON solution? If it’s not actually intended to handle all JSON you should probably call it something else.

Jon

···

On Jun 22, 2017, at 9:42 PM, Greg Parker <gparker@apple.com> wrote:

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

  I’m sorry, are you complaining about my use of Codable instead of more precisely referring to the JSON endcode/decode functionality based on it in Foundation, or are you honestly trying to say that said functionality was never intended to be a general purpose JSON solution? If it’s not actually intended to handle all JSON you should probably call it something else.

Hi Jon,

First of all, I'd like to point out that I've found your tone to be quite rude. Calling the design of Codable, that has gotten a lot of work from Apple and swift-evolution, as silly is insulting and can leave people hurt. If you have found it lacking, please say so: we're all here to discuss any feedback people have had with Swift. But please do so with respect for the people and the work behind it.

Now, concerning Codable, I find its name quite apt. It was never intended to be used a full JSON parser but as a strongly-typed Swift equivalent of Objective-C's NSCoding, which is nothing more than a framework for serializing and deserializing types into different file formats.

David.

···

On 23 Jun 2017, at 03:45, Jon Shier via swift-users <swift-users@swift.org> wrote:

Jon

On Jun 22, 2017, at 9:42 PM, Greg Parker <gparker@apple.com> wrote:

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users <swift-users@swift.org> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

--
Greg Parker gparker@apple.com Runtime Wrangler

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

David:
  I never called the design silly (though I think it’s inadequate for some important usage and makes some strange decisions), I was referring to the fact that the (apparent) official solution can’t actually decode all of the JSON people use. It’s the same reason I brought up NSISO8601DateFormatter. After years of using third party libraries or writing your own, limited, implementation, finally, finally there was an official solution from Apple. The official 8601 date formatter. Only, come to find out, it doesn’t actually handle all of 8601 and those third party libraries or custom implementations are still required if you venture outside supported scenarios. I’m concerned about the same thing happening here. Now, if JSONDecoder isn’t actually intended to serve as a general JSON parsing solution, which I think would be surprising to a lot people, then fair enough. Apple and the Swift team just need to be far more clear that that’s the case, rather letting everyone believe otherwise. And frankly, if that’s the case, I think a huge opportunity has been missed. At the same time, if / when an official solution for JSON parsing comes out, or an actual JSON representation, how will it interact with the previous implementation?
  These concerns, and the general concerns I expressed during the evolution review (which still exist) aside, this is fixable, if the Swift team is interested in doing so. However, if the limitations of JSONDecoder aren’t even seen as limitations, or interest in fixing them aren’t there, then there’s little point to continuing the discussion. Something as simple as an additional decode(_ type: from: Any) on JSONDecoder would solve the issues with decoding partially deserialized blobs or representations from other APIs. Something to help represent Any in Codable types might be useful, though I recognize that there isn’t any way to currently differentiate between Codable types and those just used by JSON.
  All of that said, my concerns mainly lie within the JSON realm. Codable works great for serialization to disk or other scenarios where I can just deal with the Data result and not have to worry about weakly typed intermediate results. I’ll certainly be using it everywhere I can. And I’m super happy that conformance is generated by the compiler rather than manually, like we had to do with Objective-C for over a decade. Even the JSON side is useful if I can control both sides of the API, which makes Swift on the server very powerful.
  So if I seem overly strident in my expression here it’s because I experience the pain of consuming poorly designed JSON in Swift on practically a daily basis and had hoped that a native implementation would alleviate that. That it doesn’t, for me and others, currently, is very disappointing. That the Swift team doesn’t seem to see the current limitations as important or at all is doubly so, since it seems like these issues will never be fixed.

Jon

···

On Jun 23, 2017, at 4:34 AM, David Hart <david@hartbit.com> wrote:

On 23 Jun 2017, at 03:45, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  I’m sorry, are you complaining about my use of Codable instead of more precisely referring to the JSON endcode/decode functionality based on it in Foundation, or are you honestly trying to say that said functionality was never intended to be a general purpose JSON solution? If it’s not actually intended to handle all JSON you should probably call it something else.

Hi Jon,

First of all, I'd like to point out that I've found your tone to be quite rude. Calling the design of Codable, that has gotten a lot of work from Apple and swift-evolution, as silly is insulting and can leave people hurt. If you have found it lacking, please say so: we're all here to discuss any feedback people have had with Swift. But please do so with respect for the people and the work behind it.

Now, concerning Codable, I find its name quite apt. It was never intended to be used a full JSON parser but as a strongly-typed Swift equivalent of Objective-C's NSCoding, which is nothing more than a framework for serializing and deserializing types into different file formats.

David.

Jon

On Jun 22, 2017, at 9:42 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

Hi Jon,

I just joined this mailing list and have tried to catch up on the history of this thread, so please excuse me if I’ve missed something.

I’m sorry the Codable API at the moment does not answer your needs — you’re clearly not the only one who’s run into this, so let’s see how we can work together to make the API better for everyone.
For one thing, in the case of grabbing a subtree of JSON as "unevaluated" or "unmapped" (as it appears to be in the metadata case), it should be fairly simple to add a `JSONDecoder.UnevaluatedJSON` type that will allow you to essentially decode that part of the tree as an `Any`. `JSONDecoder` would have knowledge of this type and would be able to return the subtree inside of it — you’d decode a property as `JSONDecoder.UnevaluatedJSON.self` and access the contents through `var value: Any?`, or something similar. This would be simple additive API, which although might not make it in the upcoming betas, should be fairly simple introduce. Would this solve that use case?

We’re also working on improving `NSISO8601DateFormatter`. I don’t think I saw it in any of your emails — what specific use case are you looking for that it doesn’t at the moment support?

— Itai

···

On 23 Jun 2017, at 9:24, Jon Shier via swift-users wrote:

David:
  I never called the design silly (though I think it’s inadequate for some important usage and makes some strange decisions), I was referring to the fact that the (apparent) official solution can’t actually decode all of the JSON people use. It’s the same reason I brought up NSISO8601DateFormatter. After years of using third party libraries or writing your own, limited, implementation, finally, finally there was an official solution from Apple. The official 8601 date formatter. Only, come to find out, it doesn’t actually handle all of 8601 and those third party libraries or custom implementations are still required if you venture outside supported scenarios. I’m concerned about the same thing happening here. Now, if JSONDecoder isn’t actually intended to serve as a general JSON parsing solution, which I think would be surprising to a lot people, then fair enough. Apple and the Swift team just need to be far more clear that that’s the case, rather letting everyone believe otherwise. And frankly, if that’s the case, I think a huge opportunity has been missed. At the same time, if / when an official solution for JSON parsing comes out, or an actual JSON representation, how will it interact with the previous implementation?
  These concerns, and the general concerns I expressed during the evolution review (which still exist) aside, this is fixable, if the Swift team is interested in doing so. However, if the limitations of JSONDecoder aren’t even seen as limitations, or interest in fixing them aren’t there, then there’s little point to continuing the discussion. Something as simple as an additional decode(_ type: from: Any) on JSONDecoder would solve the issues with decoding partially deserialized blobs or representations from other APIs. Something to help represent Any in Codable types might be useful, though I recognize that there isn’t any way to currently differentiate between Codable types and those just used by JSON.
  All of that said, my concerns mainly lie within the JSON realm. Codable works great for serialization to disk or other scenarios where I can just deal with the Data result and not have to worry about weakly typed intermediate results. I’ll certainly be using it everywhere I can. And I’m super happy that conformance is generated by the compiler rather than manually, like we had to do with Objective-C for over a decade. Even the JSON side is useful if I can control both sides of the API, which makes Swift on the server very powerful.
  So if I seem overly strident in my expression here it’s because I experience the pain of consuming poorly designed JSON in Swift on practically a daily basis and had hoped that a native implementation would alleviate that. That it doesn’t, for me and others, currently, is very disappointing. That the Swift team doesn’t seem to see the current limitations as important or at all is doubly so, since it seems like these issues will never be fixed.

Jon

On Jun 23, 2017, at 4:34 AM, David Hart <david@hartbit.com> wrote:

On 23 Jun 2017, at 03:45, Jon Shier via swift-users >> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  I’m sorry, are you complaining about my use of Codable instead of more precisely referring to the JSON endcode/decode functionality based on it in Foundation, or are you honestly trying to say that said functionality was never intended to be a general purpose JSON solution? If it’s not actually intended to handle all JSON you should probably call it something else.

Hi Jon,

First of all, I'd like to point out that I've found your tone to be quite rude. Calling the design of Codable, that has gotten a lot of work from Apple and swift-evolution, as silly is insulting and can leave people hurt. If you have found it lacking, please say so: we're all here to discuss any feedback people have had with Swift. But please do so with respect for the people and the work behind it.

Now, concerning Codable, I find its name quite apt. It was never intended to be used a full JSON parser but as a strongly-typed Swift equivalent of Objective-C's NSCoding, which is nothing more than a framework for serializing and deserializing types into different file formats.

David.

Jon

On Jun 22, 2017, at 9:42 PM, Greg Parker <gparker@apple.com >>>> <mailto:gparker@apple.com>> wrote:

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users >>>>> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Itai:

No need to apologize, I do appreciate the difficulties of designing this entire feature as quickly and completely as was required.

An intermediate JSON type would be a great fix, though most useful if it exists outside of JSONDecoder so it's useful for encoding as well. As long as it can be initialize from outside and decoded on its own, I think it could solve much of that issue.

Jo

···

On Jun 23, 2017, at 12:54 PM, Itai Ferber <iferber@apple.com> wrote:

Hi Jon,

I just joined this mailing list and have tried to catch up on the history of this thread, so please excuse me if I’ve missed something.

I’m sorry the Codable API at the moment does not answer your needs — you’re clearly not the only one who’s run into this, so let’s see how we can work together to make the API better for everyone.
For one thing, in the case of grabbing a subtree of JSON as "unevaluated" or "unmapped" (as it appears to be in the metadata case), it should be fairly simple to add a JSONDecoder.UnevaluatedJSON type that will allow you to essentially decode that part of the tree as an Any. JSONDecoder would have knowledge of this type and would be able to return the subtree inside of it — you’d decode a property as JSONDecoder.UnevaluatedJSON.self and access the contents through var value: Any?, or something similar. This would be simple additive API, which although might not make it in the upcoming betas, should be fairly simple introduce. Would this solve that use case?

We’re also working on improving NSISO8601DateFormatter. I don’t think I saw it in any of your emails — what specific use case are you looking for that it doesn’t at the moment support?

— Itai

On 23 Jun 2017, at 9:24, Jon Shier via swift-users wrote:

David:
  I never called the design silly (though I think it’s inadequate for some important usage and makes some strange decisions), I was referring to the fact that the (apparent) official solution can’t actually decode all of the JSON people use. It’s the same reason I brought up NSISO8601DateFormatter. After years of using third party libraries or writing your own, limited, implementation, finally, finally there was an official solution from Apple. The official 8601 date formatter. Only, come to find out, it doesn’t actually handle all of 8601 and those third party libraries or custom implementations are still required if you venture outside supported scenarios. I’m concerned about the same thing happening here. Now, if JSONDecoder isn’t actually intended to serve as a general JSON parsing solution, which I think would be surprising to a lot people, then fair enough. Apple and the Swift team just need to be far more clear that that’s the case, rather letting everyone believe otherwise. And frankly, if that’s the case, I think a huge opportunity has been missed. At the same time, if / when an official solution for JSON parsing comes out, or an actual JSON representation, how will it interact with the previous implementation?
  These concerns, and the general concerns I expressed during the evolution review (which still exist) aside, this is fixable, if the Swift team is interested in doing so. However, if the limitations of JSONDecoder aren’t even seen as limitations, or interest in fixing them aren’t there, then there’s little point to continuing the discussion. Something as simple as an additional decode(_ type: from: Any) on JSONDecoder would solve the issues with decoding partially deserialized blobs or representations from other APIs. Something to help represent Any in Codable types might be useful, though I recognize that there isn’t any way to currently differentiate between Codable types and those just used by JSON.
  All of that said, my concerns mainly lie within the JSON realm. Codable works great for serialization to disk or other scenarios where I can just deal with the Data result and not have to worry about weakly typed intermediate results. I’ll certainly be using it everywhere I can. And I’m super happy that conformance is generated by the compiler rather than manually, like we had to do with Objective-C for over a decade. Even the JSON side is useful if I can control both sides of the API, which makes Swift on the server very powerful.
  So if I seem overly strident in my expression here it’s because I experience the pain of consuming poorly designed JSON in Swift on practically a daily basis and had hoped that a native implementation would alleviate that. That it doesn’t, for me and others, currently, is very disappointing. That the Swift team doesn’t seem to see the current limitations as important or at all is doubly so, since it seems like these issues will never be fixed.

Jon

On Jun 23, 2017, at 4:34 AM, David Hart <david@hartbit.com> wrote:

On 23 Jun 2017, at 03:45, Jon Shier via swift-users <swift-users@swift.org> wrote:

  I’m sorry, are you complaining about my use of Codable instead of more precisely referring to the JSON endcode/decode functionality based on it in Foundation, or are you honestly trying to say that said functionality was never intended to be a general purpose JSON solution? If it’s not actually intended to handle all JSON you should probably call it something else.

Hi Jon,

First of all, I'd like to point out that I've found your tone to be quite rude. Calling the design of Codable, that has gotten a lot of work from Apple and swift-evolution, as silly is insulting and can leave people hurt. If you have found it lacking, please say so: we're all here to discuss any feedback people have had with Swift. But please do so with respect for the people and the work behind it.

Now, concerning Codable, I find its name quite apt. It was never intended to be used a full JSON parser but as a strongly-typed Swift equivalent of Objective-C's NSCoding, which is nothing more than a framework for serializing and deserializing types into different file formats.

David.

Jon

On Jun 22, 2017, at 9:42 PM, Greg Parker <gparker@apple.com> wrote:

On Jun 22, 2017, at 6:00 PM, Jon Shier via swift-users <swift-users@swift.org> wrote:

  My main concern here is that, as Swift’s official JSON parsing method, Codable should be able to handle any JSON representation and use and it doesn’t.

Is this true? Is Codable intended to be Swift's official JSON parsing system? Is Codable intended to be a general-purpose JSON parsing system?

My understanding was that Codable was designed to serialize Swift types, not to be able to import arbitrary JSON text into Swift nor to interoperate with every existing JSON API.

--
Greg Parker gparker@apple.com Runtime Wrangler

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

I’m resurrecting this thread as solutions started to be talked about and then nothing came of it. Also, I’ve recently hit this issue myself and have been looking for a solution to it.


@itaiferber mentions a solution of adding JSONDecoder.UnevaluatedJSON in a couple of posts above. This would lead to its usage to be something like:

let metadataJSON = try container.decode(JSONDecoder.UnevaluatedJSON.self, forKey: .metadata)
self.metadata = metadataJSON.any // Or possibly 'keyedAny' which would yield a [String: Any]

While I can see this being a workable solution, I wonder if a deeper solution would be better as this only effects JSONDecoder. For example; as it stands we have a method in the KeyedDecodingContainerProtocol specifically for nil decodeNil(forKey key: Key), and in the UnkeyedDecodingContainer there is decodeNil(). These two methods are specific for a particular use case that doesn’t involve trying to unbox a value to a specific type.

With that in mind what I would propose is 2 new methods added to the KeyedDecodingContainerProtocol and UnkeyedDecodingContainer that deal specifically with the case where you don’t know whats going to be returned.

KeyedDecodingContainerProtocol

public func decodeUnevaluated(forKey key: Key) throws -> [String: Any]

UnkeyedDecodingContainer

public func decodeUnevaluated() throws -> Any

Adding these methods to the protocols would allow the situation where you have values within your payload that you care about but don’t know what’s contained, it also means that it’s not limited to JSONDecoder and could be applied to any Decoder that we might want to add further down the line.

NOTE: This could also be applied to the encoder, but I’ve kept the topic specific to the Decoder as it’s the main reason for this thread.

Thanks for bringing this back up, Dale. I’ve given this a bit of further thought in my spare time (though due to resource constraints I don’t think we’ll be able to do this for Swift 5 right now). It’s possible to extend the containers (and add default implementations) to do this, but I think a slightly more elegant solution would be to just expose the Unevaluated type more generally:

public struct Unevaluated : Codable {
    public let value: Any
    public init(_ value: Any) { self.value = value }

    public init(from decoder: Decoder) throws {
        // throw a type mismatch
    }

    public func encode(to encoder: Encoder) throws {
        // throws an invalid value error
    }
}

The naming and scoping is something we’d need to figure out, but the idea is that this type can be shared among all encoders and decoders (and their containers). Just like some encoders and decoders intercept certain types to customize them (e.g. with encoding strategies), they would intercept Unevaluated as well. You would encode(Unevaluated(...)) and decode(Unevaluated.self) just as you would any other type. If the type is intercepted, the encoder/decoder does what it needs to to handle the inner value; if not, you’ll get the error thrown by default (“this encoder/decoder can’t handle Unevaluated”), which you can catch and decide how to deal with.

Aside from naming and deciding where to hang the type, I think this would be the easiest and least intrusive approach.

1 Like

Yes, this looks better, but I am not yet convinced this is the solution. A general approach for any Codable is nice and very helpful, but people run into this most often with JSON. We may need a JSON-specific solution that is a bit more accommodating. For example, with dynamic lookup stuff in place, unevaluated JSON might be better represented as a dynamic lookup type with some introspection capabilities.