All Projects → johnno1962 → TwoWayMirror

johnno1962 / TwoWayMirror

Licence: MIT license
Adapt Swift’s Mirror functionality to make it bidirectional.

Programming Languages

swift
15916 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to TwoWayMirror

Bfj
MOVED TO GITLAB
Stars: ✭ 164 (+331.58%)
Mutual labels:  json-parser
Whc model
iOS平台高效转换引擎json->model,model->json,model->Dictionary,支持模型类继承其他模型类,支持指定路径转换,不区分json的key和模型属性名称大小写,自动处理json中null
Stars: ✭ 244 (+542.11%)
Mutual labels:  json-parser
jackson-js
JavaScript object serialization and deserialization library using decorators. It supports also advanced Object concepts such as polymorphism, Object identity and cyclic objects.
Stars: ✭ 86 (+126.32%)
Mutual labels:  json-parser
Jsons
🐍 A Python lib for (de)serializing Python objects to/from JSON
Stars: ✭ 178 (+368.42%)
Mutual labels:  json-parser
Json Dry
🌞 JSON-dry allows you to serialize & revive objects containing circular references, dates, regexes, class instances,...
Stars: ✭ 214 (+463.16%)
Mutual labels:  json-parser
dora
JSON parser/explorer
Stars: ✭ 42 (+10.53%)
Mutual labels:  json-parser
Helios
A purely functional JSON library for Kotlin built on Λrrow
Stars: ✭ 157 (+313.16%)
Mutual labels:  json-parser
Cerializer
JSON Serializer using compile time reflection
Stars: ✭ 16 (-57.89%)
Mutual labels:  json-parser
Thorsserializer
C++ Serialization library for JSON
Stars: ✭ 241 (+534.21%)
Mutual labels:  json-parser
domino-jackson
Jackson with Annotation processing
Stars: ✭ 46 (+21.05%)
Mutual labels:  json-parser
Dlib
Allocators, I/O streams, math, geometry, image and audio processing for D
Stars: ✭ 182 (+378.95%)
Mutual labels:  json-parser
Json
A really simple C# JSON Parser in 350 lines
Stars: ✭ 202 (+431.58%)
Mutual labels:  json-parser
Indian-States-and-Cities-Android
Offline Android App to illustrate Auto Complete Indian cities and states text views
Stars: ✭ 19 (-50%)
Mutual labels:  json-parser
Web Database Analytics
Web scrapping and related analytics using Python tools
Stars: ✭ 175 (+360.53%)
Mutual labels:  json-parser
ajson
Abstract JSON for Golang with JSONPath support
Stars: ✭ 144 (+278.95%)
Mutual labels:  json-parser
Json To Ast
JSON AST parser
Stars: ✭ 161 (+323.68%)
Mutual labels:  json-parser
Oj
Optimized JSON
Stars: ✭ 2,824 (+7331.58%)
Mutual labels:  json-parser
representable
Maps representation documents from and to Ruby objects. Includes JSON, XML and YAML support, plain properties and compositions.
Stars: ✭ 689 (+1713.16%)
Mutual labels:  json-parser
jisoni
A native JSON parser written in pure @vlang/v
Stars: ✭ 13 (-65.79%)
Mutual labels:  json-parser
xijs
A business - oriented scene Js Library
Stars: ✭ 91 (+139.47%)
Mutual labels:  json-parser

TwoWayMirror - bidirectional Swift Mirror

*** Recent changes to Swift master indicate this is unlikely to work after Swift 5 ***

It's a frustrating limitation of Swift reflection that the Mirror type can be only used in one direction for reading values from Swift data structures. This project leverages Swift's internal implementation to remove this limitation by falling back to the original underlying RefelectionLegacy.swift functionality. Think runtime typed keypaths on steroids.

The basic api declares the following entry point:

public func reflect<T>(object: AnyObject, path: String, type: T.Type) -> UnsafeMutablePointer<T>

This will return a typed pointer to any ivar of a class object or it's containing structs, enums, collections that can be read or assigned to as if you were using a typed keypath. A subscript is defined on any class derived from NSObject for a Swift valueForKey: replacement.

public protocol SubScriptReflectable: AnyObject {}
extension NSObject: SubScriptReflectable {}
public extension SubScriptReflectable {
    public subscript<T> (path: String, type: T.Type) -> T {
        get {
            return TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee
        }
        set(newValue) {
            TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee = newValue
        }
    }
}

Example usage:

enum ExampleEnum: TwoWayEnum {
    case one, two(str: String), three(int: Int), four(int: Int, int2: Int)

    static func twDecode(data: inout TwoWayMirror, from: [String: Any]) throws {
        let ptr = data.pointer(type: ExampleEnum.self)
        switch from["case"] as! String {
        case "one":
            ptr.pointee = .one
        case "two":
            ptr.pointee = .two(str: from["let"] as! String)
        case "three":
            ptr.pointee = .three(int: from["let"] as! Int)
        case "four":
            ptr.pointee = .four(int: from["int"] as! Int,
                                int2: from["int2"] as! Int)
        default:
            throw NSError(domain: "ExampleEnum", code: -1,
                          userInfo: [NSLocalizedDescriptionKey:
                            "Invalid case in: \(from)"])
        }
    }
}
struct ExampleStruct {
    let i = 123
}
struct ContainableStruct: TwoWayContainable {
    var a1 = 0, a2 = 1
}
final class ExampleClass: NSObject, TwoWayContainable {
    let a = [98.0]
    let b = 199.0
    let c = "Hello"
    let d = ExampleStruct()
    let e = ExampleEnum.four(int: 1, int2: 9)
    let f = Date()
    let g = ["A", "B"]
    let h: [ContainableStruct]? = nil
    let i = [Int]()
    let j: [Int]? = nil
    let k: ContainableStruct? = nil
    let l = [[123, 123], [234, 234]]
    let m = ["a": [123, 123], "b": [234, 234]]
    let n = ["a": ContainableStruct(), "b": ContainableStruct()]
    let o = [["a": [123, 123], "b": [234, 234]], ["a": [123, 123], "b": [234, 234]]]
    deinit {
        print("deinit")
    }
}

if true {
    let instance = ExampleClass()

    print(TwoWayMirror.reflectKeys(any: instance))
    print(TwoWayMirror.reflectKeys(any: instance, path: "d"))

    TwoWayMirror.reflect(object: instance, path: "a", type: [Double].self).pointee += [11.0]
    print(instance["a", [Double].self])

    instance["b", Double.self] += 100.0
    print(instance.b)

    instance["c", String.self] += " String"
    print(instance.c)

    instance["d.i", Int.self] += 345
    print(instance.d.i)

    instance["e", ExampleEnum.self] = .two(str: "TWO")
    print(instance.e)

    instance["f", Date.self] = Date()
    print(instance["f", Date.self])

    let data = """
    [
      {
      "a1": 11, "a2": 22
      },
      {
      "a1": 111, "a2": 222
      }
    ]
    """.data(using: .utf8)!

    let array = try! TwoWayMirror.decode([ContainableStruct].self, from: data)
    dump(array)
}

This has been used to produce an alternative implementation of Codable for working with JSON.

let data = """
    {
    "a": [77.0, 88.0],
    "b": 999.0,
    "c": "hello",
    "d": {
        "i": 789
    },
    "f": "2018-02-14 06:39:41 +0000",
    "g": ["Hello", "World"],
    "h": [
          {
          "a1": 11, "a2": 22
          },
          {
          "a1": 111, "a2": 222
          }
          ],
    "i": [12345, 67890],
    "j": [99, 101],
    "k": {
          "a1": 1111, "a2": 2222
          },
    "m" : {
        "b" : [
          111,
          222
        ],
        "a" : [
          333,
          444
        ]
    },
    "n" : {
        "b" : {
          "a2" : 1,
          "a1" : 2
        },
        "a" : {
          "a2" : 3,
          "a1" : 4
        }
    },
    }
    """.data(using: .utf8)!

let start = Date.timeIntervalSinceReferenceDate
for _ in 0..<10 {
    let i1 = ExampleClass()
    try! TwoWayMirror.decode(object: i1, json: data)
    dump(i1)
    let json = try! TwoWayMirror.encode(object: i1, options: [.prettyPrinted])
    print(String(data: json, encoding: .utf8)!)
    let i2 = try! TwoWayMirror.decode(ExampleClass.self, from: json)
    dump(i2)
}
print(Date.timeIntervalSinceReferenceDate-start)

The JSON implementation will decode and encode composed structs and class instances, Ints, Doubles and String along with Arrays or Optionals of these and Arrays or Optionals of structs or class instances which implement the TwoWayContainable protocol (which just requires they have an init() methhod.) For writing using reflection to work (decoding) the top level object must be an instance of a class. Otherwise, a copy is taken when the object is reflected and any changes will be lost.

Automatic encoding of enums is possible but for decoding you must opt-in to the TwoWayEnum protocol and supply an implementation to initialise an enum from a dictionary.

While this approach bends a few rules it has proven to be robust making very few assumptions about the Swift reflection implementation.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].