Giter VIP home page Giter VIP logo

himotoki's Introduction

Himotoki

Join the chat at https://gitter.im/ikesyo/Himotoki

GitHub release CI Status Carthage compatible Swift Package Manager

Himotoki (紐解き) is a type-safe JSON decoding library written purely in Swift. This library is highly inspired by the popular Swift JSON parsing libraries: Argo and ObjectMapper.

Himotoki has the same meaning of 'decoding' in Japanese.

  • Just do JSON decoding (deserialization) well. JSON encoding (serialization) will not be supported going forward. 😉
  • Much simpler API.
  • Fail-fast conditional model building. This is useful for some structs with non-optional let properties.
  • No external dependencies.

Let's take a look at a simple example:

struct Group: Himotoki.Decodable {
    let name: String
    let floor: Int
    let locationName: String
    let optional: [String]?

    // MARK: Himotoki.Decodable

    static func decode(_ e: Extractor) throws -> Group {
        return try Group(
            name: e <| "name",
            floor: e <| "floor",
            locationName: e <| [ "location", "name" ], // Parse nested objects
            optional: e <|? "optional" // Parse optional arrays of values
        )
    }
}

func testGroup() {
    var JSON: [String: AnyObject] = [ "name": "Himotoki", "floor": 12 ]
    
    let g = try? Group.decodeValue(JSON)
    XCTAssert(g != nil)
    XCTAssert(g?.name == "Himotoki")
    XCTAssert(g?.floor == 12)
    XCTAssert(g?.optional == nil)

    JSON["name"] = nil
    do {
        try Group.decodeValue(JSON)
    } catch let DecodeError.MissingKeyPath(keyPath) {
        XCTAssert(keyPath == "name")
    } catch {
        XCTFail()
    }
}

⚠️ Please note that you should need to add the module name Himotoki to Decodable (Himotoki.Decodable) to avoid type name collision with Foundation.Decodable in Xcode 9 or later. ⚠️

Implementing the decode method for your models

To implement the decode method for you models conforming to the Decodable protocol, you can use the following Extractor's extraction methods:

  • public func value<T: Decodable>(_ keyPath: KeyPath) throws -> T
  • public func valueOptional<T: Decodable>(_ keyPath: KeyPath) throws -> T?
  • public func array<T: Decodable>(_ keyPath: KeyPath) throws -> [T]
  • public func arrayOptional<T: Decodable>(_ keyPath: KeyPath) throws -> [T]?
  • public func dictionary<T: Decodable>(_ keyPath: KeyPath) throws -> [String: T]
  • public func dictionaryOptional<T: Decodable>(_ keyPath: KeyPath) throws -> [String: T]?

Extraction Operators

Himotoki also supports the following operators to decode JSON elements, where T is a generic type conforming to the Decodable protocol.

Operator Decode element as Remarks
<| T A value
<|? T? An optional value
<|| [T] An array of values
<||? [T]? An optional array of values
<|-| [String: T] A dictionary of values
<|-|? [String: T]? An optional dictionary of values

Value Transformation

You can transform an extracted value to an instance of non-Decodable types by passing the value to a Transformer instance as follows:

// Creates a `Transformer` instance.
let URLTransformer = Transformer<String, URL> { urlString throws -> URL in
    if let url = URL(string: urlString) {
        return url
    }
    
    throw customError("Invalid URL string: \(urlString)")
}

let url: URL = try URLTransformer.apply(e <| "foo_url")
let otherURLs: [URL] = try URLTransformer.apply(e <| "bar_urls")

Requirements

Himotoki 4.x requires / supports the following environments:

  • Swift 4.2 / Xcode 10.1 or later
  • OS X 10.9 or later
  • iOS 8.0 or later
  • tvOS 9.0 or later
  • watchOS 2.0 or later
  • Linux is also supported

Installation

Currently Himotoki supports installation via the package managers Carthage and CocoaPods.

Carthage

Himotoki is Carthage compatible.

  • Add github "ikesyo/Himotoki" ~> 3.1 to your Cartfile.
  • Run carthage update.

CocoaPods

Himotoki also can be used by CocoaPods.

  • Add the followings to your Podfile:

    use_frameworks!
    pod "Himotoki", "~> 3.1"
  • Run pod install.

License

Himotoki is released under the MIT License.

himotoki's People

Contributors

es-kumagai avatar gitter-badger avatar ikesyo avatar joshuatbrown avatar neilpa avatar norio-nomura avatar tarunon avatar yoichitgy avatar yudoufu avatar yuta-t 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

himotoki's Issues

Unable to infer initializer's type of generic decodable type

In generic decodable type, Swift can't infer the type of initializer. For example:

struct PaginatedResponse<T: Decodable where T == T.DecodedType> {
    let id: Int
    let content: T

    static func decode(e: Extractor) -> PaginatedResponse? {
        let create = { PaginatedResponse($0) } // <- Missing argument for parameter 'content' in call

        return build(
            e <| "id",
            e <| "content"
        ).map(create)
    }
}

FYI, the code below passes build successfully.

struct PaginatedResponse<T: Decodable where T == T.DecodedType> {
    let id: Int
    let content: T

    static func decode(e: Extractor) -> PaginatedResponse? {
        let create = { id, content in
            PaginatedResponse(id: id, content: content)
        }

        return build(
            e <| "id",
            e <| "content"
        ).map(create)
    }
}

Do you have any idea to infer the type of initializer in the regular way?

None of your spec sources contain a spec satisfying the dependency: `Himotoki (~> 3.1)`.

[!] Unable to satisfy the following requirements:

- `Himotoki (~> 3.1)` required by `Podfile`
- `Himotoki (~> 3.1)` required by `Podfile`
- `Himotoki (~> 3.1)` required by `Podfile`

None of your spec sources contain a spec satisfying the dependency: `Himotoki (~> 3.1)`.

You have either:
 * mistyped the name or version.
 * not added the source repo that hosts the Podspec to your Podfile.

Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default.

My Podfile

 platform :ios, '10.0'



abstract_target 'dependencies' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  pod 'Moya/RxSwift'
  pod 'Himotoki', '~> 3.1'
  pod 'RxSwift', '~> 3.1'
  pod 'RxCocoa', '~> 3.1'
  pod 'SwiftDate', '~> 4.0'
  pod 'RealmSwift', '~> 2.8'
  pod 'RxDataSources', '~> 1.0'
  pod 'SVProgressHUD'
  pod 'Firebase/Core'
  pod 'Firebase/Messaging'

end

DecodedType is removable at Swift 2.1

Swift2.1 function is returnable Self type in class, so I think we don't need DecodedType now.

public protocol Decodable {
    /// - Throws: DecodeError
    static func decode(e: Extractor) throws -> Self
}

That is destructive change, but it has some advantages.

  1. as Decodable.
    Now we cannot cast any value to Decodable because it has associated type.
    If it remove Decodable, we will able to do it.
  2. Support subclass.(restrict)
    Himotoki don't support subclass now, because DecodedType bounds base class.
    But swift don't support override extensions function, the merit will be restrict.
  3. Integration operators.(Array, Dictionary, Optional.)
    If you aspect runtime check.

Using non final class Decodable

I have an issue since i last updated my pods and Himotoki got upgraded to 2.0.0.

I no longer can use DecodedType and I have the following scenario I'd appreciate if you can help me fix.

I am subclassing a class which I need to be decodable like such:

class A: Decodable {
    var type : Int = 0
    var title: String = ""

    init (e: Extractor) {
        type = try! e <| "type"
        title = try! e <| "title"
    }

    static func decode(e: Extractor) throws -> A {
        let type : Int = try e <| "type"
        switch type {
        case 0:
            return B(e: e)
        case 1:
            return C(e: e)
        case 2:
            return A(e: e)
        }
    }
}

class B: A {
    var total : Double = 0.0
    override init(e: Extractor) {
        total = try! e <| "total"
        super.init(e: e)
    }
}

class C: A {
    var link : String = ""
    override init(e: Extractor) {
        link = try! e <| "link"
        super.init(e: e)
    }
}

I now have a compiler error on this line static func decode(e: Extractor) throws -> A which says Method 'decode' in non-final class 'A' must return 'Self' to conform to protocol 'Decodable'

Thank you for your help in advance! Great library 👍 !

Help Parsing Objects

Hi,

Can you please help me with the following scenario?

I have a base object of type BaseObj like follows:

struct BaseObj {
   let type: Int
}

and other classes that inherit from this one

struct ObjA: BaseObj {
   let title: String
}

struct ObjB: BaseObj {
   let description: String 
}

my JSON looks like follows:

{  
   "objects":[  
      {  
         "type":1,
         "title":"test title"
      },
      {  
         "type":2,
         "description":"test description"
      }
   ]
}
}

I would like my parser to return an array of BaseObj objects correctly initialized based on the type.

How would you go about parsing this response?

I would very much appreciate your help!

Thanks in advance.

Why not serialization ?

Hi, I really appreciate your library but I can't use it in current project because it doesn't support serialization. You seem don't want to had this feature in next releases. But why ? It would definitively be a good feature.

Encoding JSON

Any plans to to add encoding implementation to framework?

Decoding array of arrays

Hi,
I have an issue decoding array of arrays,
how would you write a decode function which decode a JSON like this:

"summary": {
  "items": [[0], [1], [2]]
}

I've tried writing a struct like this:

struct Summary {
  let items: [[Int]]
}

and an extension:

extension Summary: Decodable {
  static func decode(_ e: Extractor) throws -> Summary {
    return try Summary(items: e <|| "items")
  }
}

but I'm getting an error:
Type '[Int]' does not conform to protocol 'Decodable'

I know the JSON could be built better but this is what I'm getting from the server and this is what I have to decode.

Thanks,
Oron

Linux support

It unclear from the README.md if Himotoki runs on Linux.

I have browsed around in the code and it mentions Linux several places.

Perhaps make it more clear that Linux is supported.

Crash when nested object is not a dictionary

In Himotoki 1.3.0 release.
decode function crashes when nested object is not a dictionary.

struct Group: Decodable {
    let locationName: String

    static func decode(e: Extractor) throws -> Group {
        return try Group(
            locationName: e <| [ "location", "name" ] // Parse nested objects
        )
    }
}

func test() {
    var JSON: [String: AnyObject] = [
        "location" : "foo",
    ]

    do {
        let g: Group = try decode(JSON) // crash
    } catch ...
}

It seems valueFor function (in Extractor.swift) assumes 'object' parameter is a dictionary.
In above example, string "foo" is passed as 'object' parameter then crash.

Nested params

Thanks for your great lib. How can i extract nested params. For example, i want to get the value of name in owner.

e <| "owner,name"

Transform function

Do you have any idea for applying transform function?

struct User: Decodable {
   let createdAt: NSDate?

  static func decode(e: Extractor) -> User? {
     return build(
        e <|? "createdAt" <^> NSDate.dateFromUTCString
    ).map(create)
  }
}

Ambiguous reference when trying to use a generics

Hi, I'm trying to create a generic functions that use generics that fetch a Decodable object with ReactiveCocoa.

I don't think the problem is due to ReactiveCocoa, but the Swift compiler itself.
Have you got an idea how I could solve this ?

static private func fetch<T: Decodable>(URLRequest: URLRequestConvertible) -> SignalProducer<T, NetworkError> {
    return Network.request(URLRequest)
        .attemptMap { JSON in
            do {
                // This next line is errored with "Ambiguous reference to member 'decode' 
                return .Success(try decode(JSON) as T)
            } catch let error {
                print("An error occured while decoding JSON: \(error)")
                return .Failure(.IncorrectDataReturned)
            }
        }
}

decoding a json with variable keys

I am trying to decode a JSON like this one with Himotoki in Swift 3

next: [
          {
            question: 'Fréquence',
            condition: { '==': ['answer', 'Actions'] },
          },
          {
            question: 'Fréquence',
            condition: { '>=': ['answer', 'Stop'] },
          },
          {
            question: 'Risque',
            condition: { '<=': ['answer', 'nul'] }
          }
        ]

In the condition, there is an object that looks like a dictionary, since the field name changes ('==', '<=', '>=, ...). The value is then an array of string

I have tried to decode like this

typealias ConditionType = [String]

extension ConditionType: Decodable {}

struct NextItem {
    let question: String?
    let condition: [String : ConditionType]?
}

extension NextItem: Decodable {
    static func decode(_ e: Extractor) throws -> NextItem {

        return try NextItem(
            question:  e <|? "question",
            condition: e <|-|? "condition"
        )
    }
}

I do not not how to decode the ConditionType since there is no field name for the array

How best to extract raw dictionaries?

I have some JSON where one key path returns an Array<Dictionary<String,Any>>, that is an array of [String:Any] and I want to pass this to a Transformer for manipulation into a different structure.

The issue I have is that Extractor's array and arrayOptional methods expect to return an array of Decodable T. I can see why this works for Transformer<String, URL> and the like thanks to the extensions in StandardLib.swift, but [String:Any] does not conform to Decodable.

The only way I can get this to work is by adding an extension on Dictionary as follows:

extension Dictionary: Decodable {
    public static func decode(_ e: Extractor) throws -> Dictionary<Key, Value> {
        return try castOrFail(e.rawValue)
    }
}

Is this correct? Should this be part of StandardLib.swift, or am I approaching this in the wrong way?

Restore `DecodedType` associatedtype

DecodedType associatedtype is removed in Himotoki 2.0, but that make it impossible to adopt class cluster pattern as filed in #118.

We should consider to re-introduce the requirement.

Wrong keyPath message on DecodeError.TypeMismatch

decodeValue reports the wrong keyPath.
The TypeMismatch error below should be like this

TypeMismatch(expected: Int, actual: String, keyPath: KeyPath(["displayType"]))

, instead of this

TypeMismatch(expected: Int, actual: Optional(), keyPath: KeyPath(["dialog"]))

Test Code

let json = loadJson("videoFileList_error.json") as! JSON
let _: ErrorObject.Data = try! decodeValue(json["error"]!["data"]!)
struct ErrorObject: Decodable {
    let code: Int
    let message: String
    let data: Data?
    struct Data: Decodable {
        let dialog: Dialog?
        let url: String?
        struct Dialog: Decodable {
            let title: String?
            let message: String?
            let displayType: Int?
{
    "result": null,
    "error": {
        "code": 110,
        "message": "",
        "data": {
            "dialog": {
                "title": "",
                "message": "",
                "displayType": ""  // This causes TypeMismatch because it's String instead of expected Int?.
            },
            "url": null
        }
    },
    "id": "1",
    "jsonrpc": "2.0"
}

Why Class can not decode by Himotoki?

Why Class in Swift can not decode by Himotoki?
such as :
class Hello: Decodable {
var text: String?
}

static func decode(_ e: Extractor) throws -> UserModel {
return try UserModel (
text : e <| "text",
)
}

Implementing decode for a 2-D array

I have an object Song

struct Song {
    ...
    let frequencies: [[Int]]
}

and right now I'm parsing it so:

guard let arrayOfFrequencies = e.rawValue.valueForKeyPath("frequencies") as? [AnyObject] else {
    throw customError("frequency parsing failed")
}       
guard let twoD = arrayOfFrequencies.map({ $0 as? [Int] }) as? [[Int]] else {        
    throw customError("frequency parsing failed")
}       

Is this the way I should be decoding it? Can I use Himotoki to make my life easier?

valueOptional triggers swift exception break point

When calling valueOptional with a value that might return null,
value method is called, value tries to decode a given keyPath, if the keyPath is null(which is logically correct since I called valueOptional),
value throws on DecodeError.missingKeyPath, which triggers swift exception break point.

Is there any future plans in solving this issue? maybe leading valueOptional through a different end point?

Swift 3 error

Have this entity:

struct Deal {
    public let id: UInt64

    public let name: String

    public let dateCreated: Date
    public let dateFinished: Date?

    public let ownerUser: User?
    public let currentUserIsOwner: Bool?
    public let watchingUser: Bool?

    public let status: Int?

    public let dealMembers: [User]?
    public let quantityUsers: Int?
}

in decode have error:

extension Deal: Decodable {
    public static func decode(_ e: Extractor) throws -> Deal {
        return try Deal(
            id: e <| "Id",
            name: e <| "Name",
            dateCreated: e <| "DateCreated",
            dateFinished: e <|? "DateFinished",
            ownerUser: e <|? "OwnerUser",
            currentUserIsOwner: e <|? "CurrentUserIsOwner",
            watchingUser: e <|? "WatchingUser",
            status: e <|? "Status",
            dealMembers: e <||? "DealMembers",
            quantityUsers: e <|? ""
        )
    }
}

on "dateCreated" have

Generic parameter 'T' could not be inferred

ok, try to delete this field, on "dateFinished" got this again. What is the problem?

macOS Sierra, Xcode 8, Swift 3.

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.