Japx 4.0.0

Japx 4.0.0

VlahoFilip GulanJasmin Abou AldanHrvoje Hrvoic 维护。



Japx 4.0.0

  • Infinum、Vlaho Poluta 和 Filip Gulan

Japx - JSON:API 解码器/编码器

Build Status Version License Platform

Japx

轻量级的 JSON:API 解析器,它将复杂的 JSON:API 结构扁平化,并将其转换为简单的 JSON,反之亦然。它通过将 Dictionary 转换为 Dictionary 来工作,因此您可以使用 CodableUnboxWrapObjectMapper 或者您喜欢的任何其他对象映射工具。

基本示例

对于给定的 JSON 对象示例

{
    "data": {
        "id": "1",
        "type": "users",
        "attributes": {
            "email": "[email protected]",
            "username": "john"
        }
    }
}

要将其解析为简单的 JSON,请使用

let jsonApiObject: [String: Any] = ...
let simpleObject: [String: Any]

do {
    simpleObject = try JapxKit.Decoder.jsonObject(withJSONAPIObject: jsonApiObject)
} catch {
    print(error)
}

解析器将将其转换为对象,其中所有属性都将展开到 data 对象的根目录中

{
    "data": {
        "email": "[email protected]",
        "id": "1",
        "username": "john",
        "type": "users"
    }
}

高级示例

解析关系

简单的 Article 对象,其中包含其 Author

{
    "data": [
        {
            "type": "articles",
            "id": "1",
            "attributes": {
                "title": "JSON API paints my bikeshed!",
                "body": "The shortest article. Ever.",
                "created": "2015-05-22T14:56:29.000Z",
                "updated": "2015-05-22T14:56:28.000Z"
            },
            "relationships": {
                "author": {
                    "data": {
                        "id": "42",
                        "type": "people"
                    }
                }
            }
        }
    ],
    "included": [
        {
            "type": "people",
            "id": "42",
            "attributes": {
                "name": "John",
                "age": 80,
                "gender": "male"
            }
        }
    ]
}

将被展平为

{
    "data": [
        {
            "updated": "2015-05-22T14:56:28.000Z",
            "author": {
                "age": 80,
                "id": "42",
                "gender": "male",
                "type": "people",
                "name": "John"
            },
            "id": "1",
            "title": "JSON API paints my bikeshed!",
            "created": "2015-05-22T14:56:29.000Z",
            "type": "articles",
            "body": "The shortest article. Ever."
        }
    ]
}

解析附加信息

所有没有在JSON:API 规范中定义键的嵌套对象都将保持根对象内部的完整性(对于 linksmeta 对象来说也是如此)

{
    "data": [
        {
            "type": "articles",
            "id": "3",
            "attributes": {
                "title": "JSON API paints my bikeshed!",
                "body": "The shortest article. Ever.",
                "created": "2015-05-22T14:56:29.000Z",
                "updated": "2015-05-22T14:56:28.000Z"
            }
        }
    ],
    "meta": {
        "total-pages": 13
    },
    "links": {
        "self": "http://example.com/articles?page[number]=3&page[size]=1",
        "first": "http://example.com/articles?page[number]=1&page[size]=1",
        "prev": "http://example.com/articles?page[number]=2&page[size]=1",
        "next": "http://example.com/articles?page[number]=4&page[size]=1",
        "last": "http://example.com/articles?page[number]=13&page[size]=1"
    },
    "additional": {
        "info": "My custom info"
    }
}

解析后的 JSON

{
    "data": [
        {
            "updated": "2015-05-22T14:56:28.000Z",
            "id": "3",
            "title": "JSON API paints my bikeshed!",
            "created": "2015-05-22T14:56:29.000Z",
            "type": "articles",
            "body": "The shortest article. Ever."
        }
    ],
    "meta": {
        "total-pages": 13
    },
    "links": {
        "prev": "http://example.com/articles?page[number]=2&page[size]=1",
        "first": "http://example.com/articles?page[number]=1&page[size]=1",
        "next": "http://example.com/articles?page[number]=4&page[size]=1",
        "self": "http://example.com/articles?page[number]=3&page[size]=1",
        "last": "http://example.com/articles?page[number]=13&page[size]=1"
    },
    "additional": {
        "info": "My custom info"
    }
}

使用包含列表解析

为了指定您想解析的嵌套对象,可以使用 includeList 参数。例如:

{
    "data": {
        "type": "articles",
        "id": "1",
        "attributes": {
            "title": "JSON API paints my bikeshed!",
            "body": "The shortest article. Ever.",
            "created": "2015-05-22T14:56:29.000Z",
            "updated": "2015-05-22T14:56:28.000Z"
        },
        "relationships": {
            "author": {
                "data": {
                    "id": "42",
                    "type": "people"
                }
            }
        }
    },
    "included": [
        {
            "type": "people",
            "id": "42",
            "attributes": {
                "name": "John",
                "age": 80,
                "gender": "male"
            },
            "relationships": {
                "article": {
                    "data": {
                        "id": "1",
                        "type": "articles"
                    }
                }
            }
        }
    ]
}

ArticleAuthor 可以通过引用,使用 JSON:API 规范中定义的包含来进行匹配。

let includeList: String = "author.article.author"
let jsonApiObject: [String: Any] = ...
let recursiveObject: [String: Any] = try JapxKit.Decoder.jsonObject(with: jsonApiObject, includeList: includeList)

解析后的 JSON

{
    "data": {
        "type": "articles",
        "id": "1",
        "title": "JSON API paints my bikeshed!",
        "body": "The shortest article. Ever.",
        "created": "2015-05-22T14:56:29.000Z",
        "updated": "2015-05-22T14:56:28.000Z",
        "author": {
            "type": "people",
            "id": "42",
            "name": "John",
            "age": 80,
            "gender": "male",
            "article": {
                "type": "articles",
                "id": "1",
                "title": "JSON API paints my bikeshed!",
                "body": "The shortest article. Ever.",
                "created": "2015-05-22T14:56:29.000Z",
                "updated": "2015-05-22T14:56:28.000Z",
                "author": {
                    "type": "people",
                    "id": "42",
                    "name": "John",
                    "age": 80,
                    "gender": "male"
                }
            }
        }
    }
}

使用方法

Codable

Japx 为 Swift 的包装器 Swift Codable

由于 JSON:API 对象可以拥有多个额外的字段,如元数据、链接或分页信息,因此其实际模型必须包装在 data 对象中。为了便于解析,还需要根据您的 API 规范创建封装的本地对象,该对象将包含您通用的 JSON 模型

struct JapxResponse<T: Codable>: Codable {
    let data: T
    // ... additional info like: meta, links, pagination...
}

struct User: JapxCodable {
    let id: String
    let type: String
    let email: String
    let username: String
}

let userResponse: JapxResponse<User> = try JapxDecoder().decode(JapxResponse<User>.self, from: data)
let user: User = userResponse.data

其中 JapxDecodableJapxEncodableJapxCodable 文件中定义,如下所示

/// Protocol that extends Decodable with required properties for JSON:API objects
protocol JapxDecodable: Decodable {
    var type: String { get }
    var id: String { get }
}

/// Protocol that extends Encodable with required properties for JSON:API objects
protocol JapxEncodable: Encodable {
    var type: String { get }
}

可编码和Alamofire

Japx还提供了Alamofire和Codable的包装器,可以在安装章节中描述的方式中安装。

DataRequest上使用responseCodableJSONAPI方法,它会将序列化的响应传递到回调中。此外,还有一个keyPath参数,可以仅提取嵌套的data对象。因此,如果您不需要除纯数据外的任何API侧信息,则可以创建简单对象,而无需使用包装对象/结构体。

struct User: JapxCodable {
    let id: String
    let type: String
    let email: String
    let username: String
}

Alamofire
    .request(".../api/v1/users/login", method: .post, parameters: [...])
    .validate()
    .responseCodableJSONAPI(keyPath: "data", completionHandler: { (response: DataResponse<User>) in
        switch response.result {
        case .success(let user):
            print(user)
        case .failure(let error):
            print(error)
        }
    })

可编码、Alamofire和RxSwift

Japx还提供了Alamofire、CodableRxSwift的包装器,可以在安装章节中描述的方式中安装。

使用DataRequest.rx扩展上的responseCodableJSONAPI方法,它会返回一个带有序列化响应的Single

let loginModel: LoginModel = ...
let executeLogin: ([String: Any]) throws -> Single<User> = {
    return Alamofire
        .request(".../api/v1/users/login", method: .post, parameters: $0)
        .validate()
        .rx.responseCodableJSONAPI(keyPath: "data")
}

return Single.just(loginModel)
        .map { try JapxEncoder().encode($0) }
        .flatMap(executeLogin)

安装

Cocoapods

Japx可通过CocoaPods获得。要安装它,只需将以下行添加到您的Podfile中

pod 'Japx'

我们还添加了一些更多功能,例如用于网络通信的Alamofire或Moya、用于响应式编程的Rx,以及Objective-C支持。

# Alamofire
pod 'Japx/Alamofire'

# Alamofire and RxSwift
pod 'Japx/RxAlamofire'

# Moya
pod 'Japx/Moya'

# Moya and RxSwift
pod 'Japx/RxMoya'

# Objective-C
pod 'Japx/ObjC'

与其他依赖管理器不同,您应该始终使用

import Japx

无论您选择哪种自定义集成。

注意:由于与Rx支持的问题,Japx 4.0.0版本指向Moya的development分支。如果您需要稳定的Moya支持,请使用版本3.0.0。否则,请将Podfile指向开发Moya。

platform :ios, '10.0'
use_frameworks!

target 'MyApp' do

  pod 'Moya', :git => "https://github.com/Moya/Moya.git", :branch => "development"
  pod 'Japx/RxMoya'
end

Swift 包管理器

将依赖项添加到您的 Package.swift 文件中,然后在您的目标中使用

dependencies: [
    .package(url: "https://github.com/infinum/Japx.git", .upToNextMajor(from: "4.0.0"))
]

示例 Package.swift

let package = Package(
    name: "YourDependency",
    products: [
        .library(name: "YourDependency", targets: ["YourDependency"])
    ],
    dependencies: [
        .package(url: "https://github.com/infinum/Japx.git", .upToNextMajor(from: "4.0.0")),
    ],
    targets: [
        .target(
            name: "YourDependency",
            dependencies: [.product(name: "Japx", package: "Japx")]
        )
    ]
)

我们还添加了一些其他功能,例如用于网络通信的 Alamofire 或 Moya,用于响应式编程的 Rx

// Alamofire
.product(name: "JapxAlamofire", package: "Japx")

// Alamofire and RxSwift
.product(name: "JapxRxAlamofire", package: "Japx")

// Moya
.product(name: "JapxMoya", package: "Japx")

// Moya and RxSwift
.product(name: "JapxRxMoya", package: "Japx")

根据您选择的产品,您需要导入不同的模块

// Pure Japx
import Japx

// Alamofire
import JapxAlamofire

// Alamofire and RxSwift
import JapxRxAlamofire

// Moya
import JapxMoya

// Moya and RxSwift
import JapxRxMoya

Carthage

运行 carthage update --use-xcframeworks 并导入所需的集成。纯 Japx 没有任何依赖项。

导入方式与 Swift 包管理器相同,具体取决于您选择的集成方式。

注意:由于最新的 Moya 构建不与 Carthage 配合使用,因此 Moya 集成目前不支持通过 Carthage。更多信息请查看这里

示例项目

您可以在 Nuts And Bolts 仓库 中找到使用 Codable 和 Alamofire 实现 Japx 网络的示例项目,其中包含常用代码。示例项将介绍如何使用 Japx 和 JSON:API 格式处理基本的 CRUD (创建、读取、更新、删除) 操作。要运行示例,请在 仓库 上克隆,打开 Catalog.xcworkspace,运行 Catalog 应用并在 Japx 网络部分中进行导航。

该仓库中还包括一个简单的示例项目,要运行它,请打开 Japx.xcodeproj 并检查 Example 目录以及 Japx_Example 架构。

Examples 目录中可以找到与 Cocoapods (运行 pod install)、Swift 包管理器以及 Carthage 的基本集成。

作者

Infinum 维护

Infinum

许可证

Japx可在MIT许可证下使用。有关更多信息,请参阅LICENSE文件。