2016-09-14 3 views
1

그런 식으로 뭔가를하고 싶습니다. 사전 값을 문자열 열거 형으로 액세스하고 싶습니다. 나는 사전의 첨자를 오버로드하려고 노력하고 있지만 성공하지는 못했다.아래 첨자 : 문자열 열거 형을 사용하여 사전 값에 액세스

let district = address[JsonKeys.district] 

JsonKeys은 다음과 같습니다 :

enum JsonKeys: String { 
    case key1 
    case key2 
    case key... 
} 

내 첨자 과부하는 다음과 같다 :

사전에 액세스

extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject { 
    subscript(index: FOJsonKeys) -> AnyObject { 
     get { 
      return self[ index.rawValue] as! AnyObject 
     } 
    } 
} 

나는 다음과 같은 메시지가 얻을 :

**Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'** 

어디에서 잘못 되었나요? PS

:이 할 싶지 않아 (이 오류를 수정,하지만 코드는 이런 식으로 읽을 수) :

let district = address[JsonKeys.district.rawValue] 

사전은 JSON이 AlamoFire 나에게 주어진 사전을 구문 분석됩니다. 나는 그 타입을 바꿀 수 없다고 확신한다.

+1

이것은 더 심각한 문제를 나타낼 수 있습니다. 이러한 사전을 가능한 한 빨리 구조체 또는 클래스로 파싱해야하므로 JSON 문자열 키를 처리해야하는 코드는 매우 현지화되어야합니다. 프로그램에서 둘 이상의 장소에서 이러한 키를 사용해야하는 경우 잘못된 모델 레이어가있을 가능성이 큽니다. 이것이 실패하는 이유는 "StringLiteralConvertible"이 * exact * String이 아니기 때문입니다. 현재 Swift에서이 확장을 작성하는 것은 불가능합니다 (알려진 컴파일러 제한 사항이지만, 필요할 때 언제나 다른 일을 잘못하고있는 것입니다). –

+0

이 코드의 목적은 Json 사전을 클래스로 파싱합니다. String 키를 사용하고 싶지 않습니다. String enum을 사용하는 것이 더욱 강력 해 보인다. – t4ncr3d3

+0

그건 공정 하네. 아래에 답했다. –

답변

3

가장 간단한 방법은 더 컨텍스트에 사전을 들어 단지입니다. 이 경우 컨텍스트는 "이 열거 형의 키만 있습니다." 스위프트에서 유형을 들어 올리는 것은 매우 간단합니다. 구조체에 포장하십시오.

// This could be a nested type inside JSONObject if you wanted. 
enum JSONKeys: String { 
    case district 
} 

// Here's my JSONObject. It's much more type-safe than the dictionary, 
// and it's trivial to add methods to it. 
struct JSONObject { 
    let json: [String: AnyObject] 
    init(_ json: [String: AnyObject]) { 
     self.json = json 
    } 

    // You of course could make this generic if you wanted so that it 
    // didn't have to be exactly JSONKeys. And of course you could add 
    // a setter. 
    subscript(key: JSONKeys) -> AnyObject? { 
     return json[key.rawValue] 
    } 
} 

let address: [String: AnyObject] = ["district": "Bob"] 

// Now it's easy to lift our dictionary into a "JSONObject" 
let json = JSONObject(address) 

// And you don't even need to include the type. Just the key. 
let district = json[.district] 
2

이 시도 :

extension Dictionary where Key: StringLiteralConvertible { 
    subscript(index: JsonKeys) -> Value { 
     get { 
      return self[index.rawValue as! Key]! 
     } 
    } 
} 

Key: StringLiteralConvertible으로 제약 조건을 갖는 기억의 확장 StringLiteralConvertible에 자사의 주요 순응 어떤 사전 작동합니다. (당신은 String 이외의 다른 많은 종류를 알고 StringLiteralConvertible을 준수합니다.)

첨자 self[] 호출하려면 유형 Key의 값을 전달해야합니다. index.rawValueString이며, 확장자가 항상 Key 인 것은 아닙니다.

따라서 일부 사전의 확장 기능은 다른 사전의 런타임 충돌을 일으킬 수 있습니다.


는 조금 더 형식 안전한 방법 :

protocol MyJsonKeysConvertible { 
    init(jsonKeys: JsonKeys) 
} 
extension String: MyJsonKeysConvertible { 
    init(jsonKeys: JsonKeys) {self = jsonKeys.rawValue} 
} 
extension Dictionary where Key: MyJsonKeysConvertible { 
    subscript(index: JsonKeys) -> Value { 
     get { 
      return self[Key(jsonKeys: index)]! 
     } 
    } 
} 
0

나는이 오래된 질문이다 것을 알고,하지만 난이 연장 쉽게 구현, 재사용 및 경량화를 추가 할 거라고 생각했던

public protocol UsesRawValue { 
    var rawValue: String { get } 
} 

extension JsonKeys: UsesRawValue {} 

extension Dictionary where Key: ExpressibleByStringLiteral { 
    public subscript(key: UsesRawValue) -> Value? { 
     get { return self[key.rawValue as! Key] } 
     set { self[key.rawValue as! Key] = newValue } 
    } 
} 

Based on this blog post

이 방법은 각 열거 형 대신 사전을 한 번만 확장하면됩니다. 대신 각 열거 형은 UsesRawValue을 준수해야합니다. 이제 우리는 이것을 이렇게 사용할 수 있습니다.

ajson[JsonKeys.key1] 
ajson[JsonKeys.key1] = "name" 
관련 문제