GithubHelp home page GithubHelp logo

jsoncodable's People

Contributors

andyast avatar artman avatar eungkyu avatar larsjk avatar lightsprint09 avatar matthewcheok avatar nadohs avatar narirou avatar pfandrade avatar sibljon avatar srdanrasic avatar toco avatar xmkevinchen avatar zkirill avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsoncodable's Issues

init?(JSONDictionary: JSONObject) should throw

Hi @matthewcheok,

Kudos for the library! I have just one question. Why is the initializer init?(JSONDictionary: JSONObject) a failable initializer rather than a throwing one? Sometime one might want the handle the error from the outside...

Thanks!

Reflection not supported for decoding

The Objective-C libraries I have used for JSON, such as JSONModel, support reflection when decoding. This is good because JSON requests are often simple, while the responses are complex. For example, a REST API to search for a user may accept a single user ID in the request but return a complex user model in the response.

Reflection (mirroring) is supported in JSONCodable for encoding but not decoding. The user ID can be encoded "for free" but the decoding of the user model requires many lines of code.
For example, you can do:

extension User: JSONEncodable {}

and the library will handle serialization automatically. However, the reverse doesn't work; you have to deserialize the JSON manually. For example:

extension User: JSONDecodable {
    init?(JSONDictionary: JSONObject) {
        let decoder = JSONDecoder(object: JSONDictionary)
        do {
            email = try decoder.decode("email")
            name = try decoder.decode("name")
            ...
        }
       ...
    }
}

Is there a technical issue that prevents JSONCodable from auto-deserializing JSON using JSONDecodable? I wonder if it is particularly challenging for some reason. If not, I would like to help remove this limitation.

Missing travis.

There is a travis config but no Travis setup. Travis makes contributing easier by running tests during a pull request.

Bool conversion

It seems like a simple bool conversion is not possible, or am I doing something wrong?

struct Book {
    var like: Bool?
}

extension Book: JSONDecodable {
    init(object: JSONObject) throws {
        let decoder = JSONDecoder(object: object)
        like = try decoder.decode("like")
    }
}

where JSON is simply:
{"like": "false"}

With strings it returns me an optional with a string, with bool it returns nil

Decode JSONString to Structure does not work.

The protocol extension for JSONDecodable with the failable initializer init?(JSONString: String) produce the following error message:
... Cannot find an initializer for type 'UserState' that accepts an argument list of type '(JSONString: String)' ...

To reproduce this I added the following code at the end of the Playground

do {
    let jsonString = try user.JSONString()
    if let user_the_same = User(JSONString: jsonString) {
        print("Same User: \n\(user_the_same)")
    }
} catch {
     print("Error: \(error)")
}

To fix this I could add the complete failable initializer init?(JSONString: String) to the User structure declaration, but this is not how it should work.

Do you have any Idea?

How to decode a root-level JSON array?

Is there any way to do this with the current release? I see that the JSONDecodable protocol declares a convenience init method for dictionaries, but what about for APIs that return an array as their root?

Interface for extending `JSONDecoder`?

Hi,
It's probably easiest for me to explain my use case:
The application that I work on uses an API which sends down information that looks vaguely like this:

{
   ...
  "car": {
     "make": "Honda",
     "model": "Accord"
  }
}

The application itself holds separate caches with rich models referring to "Honda Accord". During parsing, I'd like to deserialize this model into a SimpleCar model, and then perform a lookup in my cache for the richer model, throwing an error if the model is not found.

My feeling is that this should be done during parse time, so I want to be able to add this contextual information to the JSONDecoder. In the current design JSONDecoder is not extensible. What are your thoughts on:

  • Making JSONDecoder a protocol
  • Use a protocol extension to provide all of the JSON parsing functionality we have now
  • Provide a BaseJSONDecoder type implementing JSONDecoder
  • Add an associatedtype to JSONDecodable
protocol JSONDecodable {
   associatedtype Decoder: JSONDecoder
   ...
}

I'm sketching this out and I don't see a particular reason why we couldn't do this. Another possibility is making JSONDecoder open to subclassing, which I don't think it is (and object is internal).

Problem JSON encoding and decoding in Swift with JSONCodable

I have this code for os x simple app, but it does not work:

import Cocoa

struct GeoFolderData {
    var nomeCartella:String
    var nomeCommittente:String?
    var dataCreazioneCartella:NSDate
    var dataModificaCartella:NSDate
    var isActive:Bool
}

extension GeoFolderData: JSONDecodable {
    init(object: JSONObject) throws {
        let decoder = JSONDecoder(object: object)
        nomeCartella = try decoder.decode("Nome_Cartella")
        nomeCommittente = try decoder.decode("Nome_Committente")
        dataCreazioneCartella = try decoder.decode(key: "dataCreazioneCartella")
        dataModificaCartella = try decoder.decode(key: "dataModificaCartella")
        isActive = try decoder.decode("Is_Active")
    }
}

extension GeoFolderData: JSONEncodable {
    func toJSON() throws -> Any {
        return try JSONEncoder.create({ (encoder) -> Void in
            try encoder.encode(nomeCartella, key: "Nome_Cartella")
            try encoder.encode(nomeCommittente, key: "Nome_Committente")
            try encoder.encode(dataCreazioneCartella, key: "Data_Creazione_Cartella")
            try encoder.encode(dataModificaCartella, key: "Data_Modifica_Cartella")
            try encoder.encode(isActive, key: "Is_Active")
        })
    }
}

extension JSONDecoder {
    public func decode(key: String) throws -> NSDate {
        return try decode(String, transformer: JSONTransformer<StringToNSDate, StringToNSDate>)
    }
}

With these errors:
screenshot 2017-04-24 23 24 56

Is there anyone who can help me?
Thank you.

toJSON() improvement

Most of the times the default implementation of toJSON() is great, so you don't need to implement that function. Anyway if you want to change the name of just one property, you need to implement toJSON() for all the properties. It would be nice to add a method that it will ask for mapping some properties name, otherwise will use the default behavior.

example:
My object has a oneProperty property. And the server wants a one_property property. I'd like a method like:

func mapPropertyName(name: String) -> String? {
   if name == "oneProperty" { 
       return "one_property" 
   } else {
       return nil // use default implementation 
   }
}

Command failed due to signal: Segmentation fault: 11

Using XCode 7.2, I'm getting Command failed due to signal: Segmentation fault: 11 error when extension part of following code is uncommented.

struct Status {
    let version: Int
    let host: String
}

extension Status: JSONDecodable {
    func toJSON() throws -> AnyObject {
        return try JSONEncoder.create({ (encoder) -> Void in
            try encoder.encode(version, key: "version")
            try encoder.encode(host, key: "host")
        })
    }
}

Based on StackOverflow comments, this fault is due to some yet to be clearly identified NSString related function signature issues and only workaround is to use another framework that doesn't have the code.

Deprecated Method

Hi , characters.count method is deprecated since xcode 9.1 please fix them and use just "count" .

Is it possible to decode an Array?

The server returns an array of records, i.e [record1, record2].. I don't quite understand how to encode/decode an array of records. Could you give me a pointer?

Push to CocoaPods

It'd be great if this library would be available through CocoaPods! I see there's a podspec file already, so it would be simple, right?

Note: For now, anyone who wants to use it you can use the following in your Podfile:

pod 'JSONCodable', :git => 'https://github.com/matthewcheok/JSONCodable.git'

Not able to read using jsondecodable

[
{
"ID": 568,
"av": 125435865,
"ad": "2016-06-07",
"ar": 0,
"at": 0,
"ah": 0,
"aj": "te"
}
]

struct Messages {
let id: [[String:AnyObject]]?
}

Issues with Arrays

I have a structure called OTKSession that conforms to JSONDecodable, however using in it context of Array within generics does not work and I get the following error:

Client.swift:128:19: In argument type '(Result<[OTKSession]>) -> Void', '[OTKSession]' does not conform to expected type 'JSONDecodable'

Example code:

func rest<ResponseType: JSONDecodable>(_ completionHandler: @escaping (Result<ResponseType>) -> Void) {
  let value = ... // comes asynchronously
  let jsonString = String(bytes: value.0, encoding: .utf8)!
  let responseObject = try! ResponseType(JSONString: jsonString)
  completionHandler(.ok(responseObject))
}

Do I have to handle arrays separately or it is possible to incorporate them within single function? It seems like there is no way to constraint generics to Array of JSONDecodable objects.

At the moment I've added overloaded method that wraps ResponseType in array. Two methods coexist just fine but it seems like a code duplication to me which probably can be solved in more elegant way.

func rest<ResponseType: JSONDecodable>(_ completionHandler: @escaping (Result<[ResponseType]>) -> Void) {
  let value = ... // comes asynchronously
  let jsonString = String(bytes: value.0, encoding: .utf8)!
  let responseObject = try! [ResponseType](JSONString: jsonString)
  completionHandler(.ok(responseObject))
}

Support System Struct Serialiaztion (CGRext, CGPoint, etc)

My workaround:

For example: CGAffineTransform

extension JSONEncoder {
    func encode(value: CGAffineTransform, key: String) {
        object[key] = NSValue(CGAffineTransform: value)
    }
}

extension JSONDecoder {
    func decode(key: String) throws -> CGAffineTransform {
        guard let value = get(key) else {
            throw JSONDecodableError.MissingTypeError(key: key)
        }
        guard let compatible = value as? NSValue else {
            throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: NSValue.self)
        }
        guard let type = String.fromCString(compatible.objCType) where type.containsString("CGAffineTransform") else {
            throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: CGAffineTransform.self)
        }
        return compatible.CGAffineTransformValue()
    }
}

But the object is internal and get() is private, So I have to change the source code.Would you consider add these pattern in the source code? I can make a pull request. But I think maybe there is a more elegant solution.

Expanded Readme Content

I think the readme does not go in depth enough into some of the features of JSONCodable..
Such as: for Decoding:

 init(object: JSONObject) throws  {
          ....
          let decoder = JSONDecoder(object: object)                  
          likes = try decoder.decode("properties[0].likes")
          ....
}

Basically explaining that this feature exists, decoding for JSON using "." and "[index]" accessors to navigate dictionaries and arrays.

Unable to call toJSON on multi-dimensional arrays

Perhaps I'm missing something, but it doesn't seem like multi-dimensional arrays are supported with the JSONEncodable protocol. For example, this test fails:

class TwoDimensionalArrayClass: JSONEncodable {
    var array2D: [[String]]?
}

class JSONCodableTests: XCTestCase {
    
    func testTwoDimensionalArray() {

        let twoDim = TwoDimensionalArrayClass()
        twoDim.array2D = [["One", "Two", "Three"], ["Four", "Five", "Six"], ["Seven", "Eight", "Nine"]]
        
        do {
            try twoDim.toJSON()
        }
        catch {
            print(error)
            XCTFail()
        }
    }
}

Any help or suggestion would be greatly appreciated.

`JSONDecodable` with static `parse` method?

Hi,
Right now the definition for JSONDecodable requires an implementation of an initializer. This has a couple of drawbacks IMO:

  • Enums with associated values can't conform to JSONDecodable (see #29)
  • For classes, JSONDecodable has to be conformed to in the main class declaration, not in an extension. This means that classes defined in ObjC can't conform to JSONDecodable in a Swift extension, for example.

My question is: what are opinions on a breaking change to moving to a definition of JSONDecodable that looks like this:

protocol JSONDecodable {
    static func parse(decoder: JSONDecoder) throws -> Self
}

I know that this would be a breaking change, but are there other reasons why this is a bad idea?

to and from JSON string

The .JSONEncode() method currently encodes to a dictionary. It would be nice if you added a couple of helper functions that use NSJSONSerialization to convert the dictionary to a JSON string (and the other way around).

If you do so, I think that method should be called JSONEncode() and the original method should be called something like .toDictionary()

Warning when building with Carthage

Hi @matthewcheok, @Nadohs.

I really love this framework. I have used it on several projects. I have a small issue where when building the project with Carthage xcodebuild will throw some warnings:

carthage update
--- xcodebuild: WARNING: Unable to open project file '/Users/user/Development/Project/Carthage/Checkouts/JSONCodable/JSONCodable.playground' in workspace '/Users/user/Development/Project/Carthage/Checkouts/JSONCodable/JSONCodable.xcworkspace'.
--- xcodebuild: WARNING: Unable to open project file '/Users/user/Development/Project/Carthage/Checkouts/JSONCodable/JSONCodable.playground' in workspace '/Users/user/Development/Project/Carthage/Checkouts/JSONCodable/JSONCodable.xcworkspace'.

Just thought I'd let you know for future releases.

Thanks again!

ambiguous decoder for type UInt64

Hi,

First of all, thanks for sharing this amazing library.

I'm using some structs with UInt64 and that caused a compilation error indicating references to ambiguous decode functions.

My workaround was to extend JSONDecoder but this does not work too. Compilation works fine but it throws error while decoding.

private struct StorePromotionItem {
    let banner: String
    let icon: String
    var saleOptionId: UInt64?
    var productOfferId: UInt64?
}

extension StorePromotionItem: JSONDecodable {
    init(object: JSONObject) throws {
        let decoder = JSONDecoder(object: object)
        banner = try decoder.decode("banner")
        icon = try decoder.decode("icon")
        saleOptionId = try decoder.decode("sale_option_id")
        productOfferId = try decoder.decode("product_offer_id")
    }
}
extension JSONDecoder {
    public func decode(key: String) throws -> UInt64 {
        let JSONTransformerStringToUInt64 = JSONTransformer<String, UInt64>(
            decoding: {
                UInt64($0)
            },
            encoding: {
                String($0)
        })

        return try decode(key, transformer: JSONTransformerStringToUInt64)
    }
}

Thanks,

tvOS, watchOS targets?

Hi,

Is there a limitation to not include these 2 options? Also, the iOS and OSX one are defaulting to "latests SDK release, which is a problem sometimes"

JSONDecoder is ambiguous for type lookup

JSONDecoder is now a part of Foundation on iOS 11 and there is no way to use JSONDecoder anymore because there are now two of them. I have a code that extends both JSONDecoder and JSONEncoder and both throw an error on Xcode 9 beta:

'JSONDecoder' is ambiguous for type lookup in this context

It seems like JSONCodable will have to prefix its classes.

Improve error messages

Preface: I've looked at a whole bunch of different JSON libraries for a talk I gave last week on JSON parsing in Swift. The talk was partly about different libraries, and partly about the JsonGen code generator I've build. Although I didn't mention JSONCodable in the talk, it is in my opinion one of the best JSON libraries I've looked at.

One of the very few features I miss in JSONCodable is verbose error messages. In JsonGen I try to include as much information as possible about errors in the JSON. So it's easier to debug problems in JSON coming from the server.

These two features in particular:

  1. Include the complete "path" of the error.
  2. Collect all errors, instead of stoping at the first error.

From my post on error messages, these are the types of error messages JsonGen generates:

2 errors in Blog struct
 - subTitle: Value is not of expected type String?: `42`
 ▿ posts: 2 errors in array
    ▿ [1] 1 error in Post struct
       - title: Field missing
    ▿ [2] 3 errors in Post struct
       - title: Value is not of expected type String: `1`
       - body: Value is not of expected type String: `42`
       ▿ author: 2 errors in Author struct
          - name: Field missing
          - email: Field missing

Feel free to close this issue if this isn't something you're interested in for JSONCodable. It's just something I wanted to bring to your attention.

Decoding values within child keys?

Given the following data structure, how would I extract values nested inside multiple keys?

{
    "links": {
         "some_link": "http://blah.com/",
         "other_link": "http://derp.com/"
    }
}

With other frameworks like Argo, I can pull out values from child keys using syntax like ["links", "some_link"]. I couldn't find anything similar on the decode() method.

JSONString: line 19 tries casting to AnyObject while return type of the toJSON method is Any

Hi Matthew,

excuse me if I am wrong (I am fairly new to Swift), but it seems to me that this line is a problem in JSONString(line 19):

let json = try toJSON() as! AnyObject

I understand that you use it later for NSJSONSerialization, but toJSON returns Any which cannot be casted toAnyObject.

Is it a bug of the Codable library or do you have any workaround in mind?

Thanks,
Anatoli

Use proper SemVer in release version tags

The tag of each version should use proper Semantic Versioning, or it won't work with tools like Carthage, which expect valid version tags.

For example: the latest version is tagged 3.0. This is not valid SemVer. It should be 3.0.0.

Not sure if it's worth rewriting the current tags, but it's definitely something to keep in mind for the future.

Thanks

JSONEncoder.encode(_:,key:) fails to compile with value argument cast to JSONEncodable

This fails to compile:

let val: JSONEncodable = SomeTypeThatImplementsJSONEncodable()
public func toJSON() throws -> AnyObject {
    return try JSONEncoder.create { encoder in
        try encoder.encode(val, key: "SomeKey")
    }
}

The error given is "Cannot invoke 'encode' with an argument list of type '(JSONEncodable, key: String)'." This seems to be because the following method on JSONEncoder is marked private:

private func encode(value: JSONEncodable, key: String) throws

Is there some reason this shouldn't be marked public?

Working with keys that have '.' (dot) in it

It seems that after pull request #53 JSONCodable got functionality for encoding/decoding nested types (9782c0f). However the problem we are having is that our API JSON responses have some keys with . in them. This makes it impossible to encode (and probably parse) the JSONs using the library.

For example this JSON is not parseable correctly with the lib:

{
   "one.key" = "value"
}

Is there a way to fallback to a more low-level approach avoiding the key.components(separatedBy: ".") part in each of func encode(_ value: key:) overload?

Problems when the classes to extend with JSONDecodable have a defined initializer.

In XCode 7.3 (swift 2.2), adding an initializer to the example class in the playground class to "Decode" breaks JSONDecodable. The same error happens when using the latest DEVELOPMENT-SNAPSHOT-2016-03-01-a version through command line from GitHub.

class Company {
    let name: String
    var address: String?

    init(n: String, a: String?) {
        name = n
        address = a
    }
}
extension Company: JSONDecodable {
     init(object : JSONObject) throws {
        let decoder = JSONDecoder(object: object)

        name = try decoder.decode("name")
        address = try decoder.decode("address")

    }
}

with two (incompatible) errors at the init(object:) in the extension:
A. Designated initializer cannot be declared in an extension of 'Company'; did you mean this to be a convenience initializer?
As such:

extension Company: JSONDecodable {
     convenience init(object : JSONObject) throws {
        let decoder = JSONDecoder(object: object)

        name = try decoder.decode("name")
        address = try decoder.decode("address")

    }
}

B. Initializer requirement 'init(object:)' can only be satisfied by a required initializer in the definition of non-final class 'Company'

JSONEncodable: Enum conversion doesn't work with toJSON() default implementation

I have a String enum in my struct, but encoding to JSON is failing with JSONEncodableError.ChildIncompatibleTypeError.

It is possible to reproduce the same issue in the current test suite by commenting out toJSON() implementation in the Fruit struct.

The problem lies in the default implementation of JSONEncoder.create in the switch (value) because an enum does not conform to JSONEncodable.

Tried to fix it in several ways, but unfortunately all failed at the same spot - extension of protocol 'RawRepresentable' cannot have an inheritance clause...

Enums with associated values can't be made decodable.

If I had a enum (liberally stolen from the Apple docs) like this:

enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)
}

Then it can't be made to inherit from JSONDecodable according to the compiler, although it actually gives you a slightly weird error about failable init methods.

Swift 5 support.

When i try to update my project to swift 5.0. I cant build with carthage with error:
SWIFT_VERSION '3.0' is unsupported, supported versions are: 4.0, 4.2, 5.0. (in target 'JSONCodable iOS' from project 'JSONCodable')

Please update the library for support swift 5. Thank you very much!

2.1 not on CocoaPods

Latest version on CocoaPods is 2.0.

Side note (in case this may help others):

Xcode crashes when adding a init(object: JSONObject) throws to an extension. When using the 2.0 version of JSONCodable which has the failable initializer in the protocol.

new release please? :)

Hi there,

could we have a new release with the latest fixes/additions please? :)

thx!

Decode array of array fail

For example:

struct model {
    let areas: [[Float]]
  }

 extension model: JSONDecodable {
    init(object: JSONObject) {
        do {
            let decoder = JSONDecoder(object: object)
            areas = try decoder.decode("areas")
            // Compiler error:  Ambiguous reference to member 'decode'
        }catch{
            fatalError("\(error)")
        }
    }

}

Using JSONCodable for classes

I'm trying to use JSONCodable with swift classes (instead of structs) and cannot get it working.

class User {
    var id: Int = 0
    var name: String = ""
    var email: String? = nil
    var friends: [User] = []
    var website: NSURL?
}

extension User: JSONCodable {
    init?(JSONDictionary: [String : AnyObject]) {
        do {
            id = try JSONDictionary.decode("id")
            name = try JSONDictionary.decode("full_name")
            email = try JSONDictionary.decode("email")
            friends = try JSONDictionary.decode("friends")
            website = try JSONDictionary.decode("website", transformer: JSONTransformers.StringToNSURL)
        }
        catch {
            print(error)
            return nil
        }
    }
}

Swift complain "Designated initializer cannot be declared in an extension of User; did you mean this to be a convenience initializer?". If I make it convenience initializer, it would then complain about required. If I use required, it then complains more ...

Any help is greatly appreciated. (Sorry if this is a stupid question, I'm not familiar with Swift compiler...)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.