Using valueForKeyPath on NSDictionary if a key starts the @ symbol?
Asked Answered
H

5

19

I want to use valueForKeyPath on my NSDictionary, but the problem is that one of the keys is a string that starts with the @ symbol. I have no control over the naming of the key.

I'm having problems trying to create the key path as I'm getting a format exception, even when trying to escape the @ symbol:

This works fine:

[[[dict objectForKey:@"key1"] objectForKey:@"@specialKey"] objectForKey:@"key3"]

However none of these work:

[dict valueForKeyPath:@"[email protected]"]
[dict valueForKeyPath:@"key1.@@specialKey.key3"]

Any ideas?

Thanks,

Mike

Homeward answered 14/10, 2009 at 17:39 Comment(1)
So basically your dict objectForKey:@"key1" is an NSDictionaryObject too. And that dict objectForKey:@"key3"Hawger
P
21

you shouldn't be using @ signs with your key names if you want to use key value coding.

apple's guidelines for key names are as follows:

Keys must use ASCII encoding, begin with a lowercase letter, and may not contain whitespace.

You'll have to find a workaround to reformat the key string whereever you're getting your keys from to be KVC compliant.

Paedo answered 14/10, 2009 at 18:33 Comment(2)
Ah okay then! I thought some form of escaping needed to happen but thinking about it, of course something starting with @ can't be KVC compliant! Cheers!Homeward
np. it'll also save you a lot of headache when you use other classes and technologies that use KVC, like core data.Paedo
M
13

Just to update this old question a little...

The reason that these:

[dict valueForKeyPath:@"[email protected]"]
[dict valueForKeyPath:@"key1.@@specialKey.key3"]

...fail is that any "@" symbols in a key path are interpreted as being collection's operators as with:

[dict valueForKeyPath:@"[email protected]"] // returns the sum of all 'key3' values
[dict valueForKeyPath:@"[email protected]"] // returns the average of all 'key3' values

The nested key calls:

[[[dict objectForKey:@"key1"] objectForKey:@"@specialKey"] objectForKey:@"key3"]

... work because a single key is not processed as a key path.

Malynda answered 10/12, 2012 at 20:7 Comment(0)
K
2

If you have no control over the naming, how about adding a category with a properly named key that simply returns/sets the weird key?

Kokoruda answered 14/10, 2009 at 21:29 Comment(1)
that's a good idea. but given what KVC is supposed to do, i don't see how that is even necessary. KVC is a practice of sorts to make naming getters and setters consistently so you can access properties. so if you can't name a property with an @ sign in it, then you should never get into a situation where it'd be a part of a keypath either.Paedo
S
0

I see that there are 2 ways

Swizzle

You can swizzle the valueForKeyPath on NSDictionary to remove the @ symbol, remember to account for @sum, @average, ...

Override if you're using Mantle

Override + (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary on MTLJSONAdapter, traverse all the keys and remove the @ symbol

Splendid answered 27/4, 2015 at 4:50 Comment(0)
G
0

In my humble opinion, the whole discussion here goes the wrong way Accessing entries in an NSDictionary via key paths - is simply not part of KVC protocol.

KVC defines how to name your properties of an object, so that KVC can work. an entry in an NSDictionary is not a property, and has no name. NSDictionary adds its bit of magic to the KVC-like behaviour, by "pretending" the keys of its entries are like 'properties' of the dictionary.

Alas, properties have different naming conventions and limitations than dictionary keys.

If you cannot force the dictionary keys to conform with KVC-supported property names - break your key paths, and use the accessors instead where in doubt.

That, I think, should be the safest way to go. KVC is generally a "nicety" being able to shorten your code - but it does NOT provide any functionality you cannot have otherwise (as you demonstrated yourself).

Grayce answered 17/9, 2020 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.