本文介绍了Swift通用对象/ JSON序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

tl; dr 您是否可以一般地使用符合协议的初始化方法实例化对象,同时还保留对象的预期类型?我正在尝试的是seg错误的编译器。

tl;dr Can you generically instantiate an object using a conforming protocol's initializer method while also still preserving the object's intended type? What I am trying now is seg faulting the compiler.

在我正在编写的库中,我试图完成使用Swift有限内省特性对对象进行一般序列化/反序列化的目标。

In a library I am writing, I am trying to accomplish a goal of generically serializing/deserializing objects using Swift's limited introspection features.

以下是使用反射设置变量值的全局函数的代码。如果它找到一个字典,它会尝试协调嵌套结构:

Here is the code for a global function that sets a variable's value using reflection. It attempts to reconcile for nested constructs if it finds a dictionary:

func model__setValue<T where T: NSObject, T: Serializable>(value: AnyObject, forSerializationKey key: String, model m: T) {
    let varNames = object__getVarNames(mirror: reflect(m)) // Gets a list of this object's variable names
    if let i = find(m.serializationKeys, key) {
        if value is [String : AnyObject] {
            // This allows us to have nested dictionary representations
            // of Serializable constructs and have them init properly
            let t1 = reflect(m)[i].1.valueType as NSObject.Type
            if t1 is Serializable.Type {
                let t2 = t1 as Serializable.Type
                let finalObj = t2(dictionary: value as [String : AnyObject]) // Segmentation fault: 11
                m.setValue(finalObj, forKey: varNames[i])
            }
        } else {
            m.setValue(value, forKey: varNames[i])
        }
    }
}

几件事情要解释:

Couple of things to explain:


  1. Serializable 是一个协议定义包括init(字典)的方法。它被想要被序列化的对象所采用。
  2. Serializable 还指定了序列化键的计算属性或一个用作对象变量的字典键的字符串列表,并且必须是变量名与序列化键的一对一映射。为什么要这样做?有时候,API调用返回键实际上不再有意义(模式腐烂?),或者我只是想以不同的方式命名变量,因为我不喜欢太多的下划线。

  3. 该模型必须是 NSObject 并符合 Serializable 。为什么?为了实现这样的泛型,我需要能够在不知道标识符的情况下写入对象的字段。 Swift没有办法做到这一点本身,所以NSObject的子类化是一个很好的折中方案。为什么不具有这些功能的根对象?
  1. Serializable is a protocol defining methods including init(dictionary). It is adopted by objects that want to be (de)serialized.
  2. Serializable also specifies a computed property of "serialization keys" or a list of strings that are used as the dictionary keys for the object's variables and must be a one-to-one mapping of variable names to serialization keys. Why do this? Sometimes, API calls return keys that don't really make sense anymore (schema rot?), or I just want to name a variable differently, as I don't tend to enjoy underscores too much.
  3. The model has to both be an NSObject and conform to Serializable. Why? To achieve generics like this, I needed the ability to write to an object's fields without know their identifiers ahead of time. Swift doesn't have a way to do this natively, so subclassing NSObject is a good compromise. Why not have a root object with those features anyway?

我的目标是从他们的JSON表示(即使使用不同的键和变量名称,即使是嵌套对象也是如此)。因此,给它一个对象它是键列表,并使协议的 init 调用另一个通用构造函数,它遍历对象的变量并每次调用它。巴姆。就这样。现在,您可以解析所有(JSON)API中的所有内容,并编写极少或不需要的解析逻辑。我也想同样适用于序列化程序,这样 toDictionary 也只需要一个泛型方法调用。

My goal with this is stupid easy, low overhead construction of objects from their JSON representation (even with different keys and variable names, even with nested objects). So give an object it's list of keys and make the protocol's init call another generic constructor function which iterates over the object's variables and calls this each time. Bam. That's all. Now you can parse all the things from all the (JSON) APIs and write extremely little or no parse logic. I wanted the same to apply to a serializer too, such that toDictionary also requires just one generic method call.

在这个函数中,我们使用反射动态地设置对象的值。首先,我们确保这是设置对象的有效关键。接下来,我们确定我们想要设置的值是否是字典。如果是这样,我想抓住 target 对象的类型来查看它是否实际上也是一个 Serializable 符合类型。如果这个字段是,我们可以使用这个字典递归构造它(这不会像刚才解释的那样工作)。如果不是,或者该值不是字典,只需使用Cocoa的setValue:forKey:方法。

In this function, we are setting an object's value dynamically using reflection. First, we make sure this is a valid key to set on the object. Next, we determine whether or not the value we want to set is a dictionary. If it is, I want to grab the target object's type to see if it is in fact also a Serializable conforming type. If that field is, we can use this dictionary to recursively construct it (this doesn't work as explained shortly). If it isn't, or the value isn't a dictionary at all, just use Cocoa's setValue:forKey: method.

唉,我无法获得嵌套对象部分的工作是非常正确的,我还没有弄清楚我想要做什么是不可能的,或者我只是做错了。我也担心我的让finalObj 行无效,尽管Xcode没有错误。我的思考过程就是调用 Serializable init 构造函数,并继续我的快乐之路。我认为这个问题是Swift的静态类型性质,编译器实际上并不知道init将返回的最终类型(尽管Xcode在该行中以相同颜色突出显示了 t2 因为它关键字...)。我不确定是否有方法可以说t1是 NSObject 也符合 Serializable 。我认为我现在试图用 t2 来进行尝试,并且cast会丢失类型信息,从而导致seg错误,尽管Xcode在我真的去之前不会抱怨它构建。

Alas, I can't get the nested object part to work quite right, and I haven't figured out if what I am trying to do is just impossible or if I am just doing it wrong. I'm also concerned my let finalObj line isn't valid, despite Xcode's lack of error on it. My thought process is just call Serializable's init constructor and go on my merry way. I assume the issue with this is Swift's statically typed nature and the compiler not actually knowing what final type the init will return (although, Xcode highlight's t2 in that line in the same color as it does keywords...). I am not really sure if there's a way to say t1 is an NSObject that also conforms to Serializable. I think what I am attempting now with t2 and the cast is losing type information and thus causing the seg fault, though again Xcode doesn't complain about it until I actually go to build.

我尝试了几种协调这个嵌套的 Serializable 的方法,但一直未能解决方案。任何人都可以协助?

I've tried several ways of reconciling this nested Serializable thing and haven't been able to come up with a solution. Would anyone be able to assist?

推荐答案

看看这篇文章,其他人做了一些类似于你所尝试的事情。它可能会对你有所帮助,或者它实际上只是你正在寻找的内容:

Check out this article, somebody else did something just like what you are trying. It might help you, or it might actually be just what you are looking for:https://www.weheartswift.com/swift-objc-magic/

这篇关于Swift通用对象/ JSON序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 14:58