2016-09-14 8 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]! 
     } 
    } 
} 

は、拡張子がStringLiteralConvertibleへの鍵準拠して任意の辞書のために働く、Key: StringLiteralConvertibleとして制約を持つことを覚えておいてください。 (あなたはStringStringLiteralConvertibleに準拠以外の多くの種類を知っている。)

添字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" 
関連する問題