-
Notifications
You must be signed in to change notification settings - Fork 0
/
JSONMirror.swift
98 lines (87 loc) · 3.01 KB
/
JSONMirror.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//
// JSONMirror.swift
// JSONMirror
//
// Created by Roman Tkachenko on 21/06/2019.
// Copyright © 2019 AwesomeDeveloper. All rights reserved.
//
import Foundation
class JSONMirror {
// MARK: - Internal
struct Options: OptionSet {
let rawValue: Int
static let excludeEmptyFields = Options(rawValue: 1 << 0)
}
class func reflect<T>(_ any: T, options: Options? = nil) -> Any? {
return reflect(element: any, options: options)
}
// MARK: - Private
private class func reflect<T>(element: T, options: Options?) -> Any? {
guard let result = value(for: element, options: options) else {
return nil
}
switch result {
case _ as [Any], _ as [AnyHashable: Any]:
return result
default:
return [result]
}
}
private class func value(for any: Any, options: Options?) -> Any? {
let mirror = Mirror(reflecting: any)
if mirror.children.isEmpty {
switch any {
case _ as Int, _ as Int64, _ as Int32, _ as Double, _ as Float, _ as String, _ as Bool:
return any
case _ as Optional<Any>:
if options?.contains(.excludeEmptyFields) ?? false {
return nil
} else {
fallthrough
}
default:
return String(describing: any)
}
} else if let displayStyle = mirror.displayStyle {
switch displayStyle {
case .class, .dictionary, .struct:
return dictionary(from: mirror, options: options)
case .collection, .set, .tuple:
return array(from: mirror, options: options)
case .enum, .optional:
return value(for: mirror.children.first!.value, options: options)
@unknown default:
print("not matched")
return nil
}
} else {
return nil
}
}
private class func dictionary(from mirror: Mirror, options: Options?) -> [AnyHashable: Any] {
return mirror.children.reduce(into: [AnyHashable: Any]()) {
var key: AnyHashable!
var value: Any!
if let label = $1.label {
key = label
value = $1.value
} else {
let array = self.array(from: Mirror(reflecting: $1.value), options: options)
guard 2 <= array.count,
let newKey = (array[0] as? AnyHashable) else {
return
}
key = newKey
value = array[1]
}
if let value = self.value(for: value!, options: options) {
$0[key] = value
}
}
}
private class func array(from mirror: Mirror, options: Options?) -> [Any] {
return mirror.children.compactMap {
value(for: $0.value, options: options)
}
}
}