另一个键的键的Swift设置值

时间:2016-03-26 17:36:01

标签: swift

我正在开展一个项目,我将XML响应转换为对象的实例。为了这个问题,根元素被映射到“参数”,子元素被映射到“子参数”,类设置就像这样。

class MyClass : NSObject {
  var Parameter = Parameter()
}

class Parameter: NSObject {
  var Subparameter = String()
}

我的问题是这个...如何使用setValue(value,key)设置MyClass.Parameter.Subparameter的值?

提前感谢您的协助。

1 个答案:

答案 0 :(得分:1)

如果你的类是NSObject的子类,那么你喜欢键值编码(KVC),你可以object.setValue(forKey:)

如果您使用具有子元素的某些元素处理XML,那么诀窍就是如何让解析器跟踪执行setValue(forKey:)的对象。我个人经常会维护一堆对象:

考虑这个XML:

<people>
    <person>
        <name>Rob</name>
        <address>
            <address1>1 Main Street</address1>
            <address2 />
            <city>Pasadena</city>
            <state>CA</state>
        </address>
    </person>
    <person>
        <name>Rachel</name>
        <address>
            <address1>1 Broadway</address1>
            <address2>Apt 1</address2>
            <city>New York</city>
            <state>NY</state>
        </address>
    </person>
</people>

考虑这个类层次结构:

class Person : NSObject {
    var name: String?
    var address: Address?
}

class Address: NSObject {
    var address1: String?
    var address2: String?
    var city: String?
    var state: String?
}

因此,当我输入<person>时,我将Person对象添加到我的堆栈中。当我退出</person>时,我从堆栈中弹出Person并相应地更新父对象。同样,当我输入并存在<address></address>时,我将Address个对象从我的堆栈中推出并再次弹出,再次相应地更新父对象。使用这种模式,在任何给定的时间点,堆栈中的最后一项始终是&#34;当前&#34;对象,所以每当遇到PersonAddress之外的其他元素时,我只是用我收到的任何字符串值更新当前对象的值。

因此,您可以使用以下方法解析它:

var results: [Person]?
private var objectStack = [NSObject]()
private var currentString: String?

func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
    if elementName == "people" {
        objectStack = [Person]()
    } else if elementName == "person" {
        objectStack.append(Person())
    } else if elementName == "address" {
        objectStack.append(Address())
    } else {
        currentString = String()
    }
}

func parser(parser: NSXMLParser, foundCharacters string: String) {
    currentString? += string
}

func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    if let foundString = currentString {
        objectStack.last?.setValue(foundString, forKey: elementName)
        currentString = nil
    } else if let lastObject = objectStack.last where lastObject is Address {
        objectStack.removeLast()
        if objectStack.isEmpty {
            results = lastObject as? [Person]
        } else {
            objectStack.last?.setValue(lastObject, forKey: elementName)
        }
    }
}

func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
    print(parseError)
}

现在这是一个基本的例子。例如,我不检查当前对象是否具有该elementName的属性,因此如果XML包含的元素名称与对象不对应,则上述内容将崩溃属性。我也不检查element属性的类型来对已解析的字符串值进行适当的转换。所有这些都很容易添加,但我希望尽可能简洁。

但它说明了基本的想法:你需要

  • 跟踪一些&#34;堆栈&#34;自定义对象,以便您知道要在didEndElement;
  • 中更新的对象
  • didStartElement中进行特殊处理,以了解是否要实例化自定义对象并将其推送到堆栈,或者是否要解析某些文本值以仅更新当前对象;
  • didEndElement中进行特殊处理,以了解您是否正在更新自定义对象的值,或者您是否已完成某些自定义对象,将其从堆栈中弹出并更新父自定义对象中的某些值(现在在堆栈的末尾)。

如果这个堆栈概念太混乱,你可以通过跟踪各种属性来完成同样的事情,具体取决于你碰巧在什么级别的XML树。我个人认为堆栈模型很有吸引力。

无论如何,一旦你负责跟踪你在XML树中的位置,你就可以setValue(forKey:)来更新一个顶级元素或一些子元素的值。