使用restkit将JSON键与前导“@”映射

时间:2014-06-18 16:21:28

标签: ios json restkit-0.20

我正在使用rest kit 0.20.3进行一些项目,现在没有问题。 现在我面临的问题是JSON密钥导致“@”导致Restkit在映射数据时崩溃。

JSON看起来像这样:

{
    "city": {
        "@name": "Charles Redirection City",
        "@nameUrl": "Charles Redirection City",
        "lat": "52.5238",
        "long": "13.4119",
        "zipCode": "666"
    },
    "categories": {
        "@count": "20037",
        "category": [
            {
                "@count": "2326",
                "@hasChildren": "true",
                "id": "15777",
                "name": "Sticky-Categorie"
            }
        ]
    },
    "additional": {
        "dataKey": [
            {
                "@key": "log-start",
                "$": "home"
            }
        ]
    }
}

使用

映射应用程序崩溃
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFDictionary 0x95b5360> valueForUndefinedKey:]: this class is not key value coding-compliant for the key hasChildren.'

因为它试图在字典上调用hasChildren。我的映射看起来像这样:

[mapping addAttributeMappingsFromDictionary:@{
                                              @"id"                 :   @"categoryId",
                                              @"name"               :   @"name",
                                              @"@hasChildren"       :   @"doesHaveChildren",
                                              @"@count"             :   @"numbersOfJobs",
                                              @"trackingName"       :   @"trackingString"
                                              }];

我已经尝试应用我自己的映射,但遗憾的是它没有用。 是否在RestKit中不支持@我已经使用NSJSONSerialization解析了JSON,它运行良好,但我真的很喜欢RestKit的映射功能,并希望避免编写自己的映射:)

4 个答案:

答案 0 :(得分:0)

Key-Value Coding中使用了@

  

集合运算符是作为参数传递给valueForKeyPath:方法的专用键路径。运算符由前面带有at符号(@)的字符串指定。

我不知道告诉NSDictionary忽略它的方法。您可以在服务器端删除JSON中的@,也可以在收到它之后但在将其交给RestKit的映射器之前删除。

答案 1 :(得分:0)

这个想法是,例如使用新密钥重新构建字典,新密钥不会包含任何@个字符。这就是在<{1}}中读取值的方法,其中包含NSDictionary个字符。

我只需将顶部@的{​​{1}}替换为新词典的顶级@

_

注意:这只是一个仅在我的代码中处于顶层的概念构思,您可以使用它来创建递归算法来替换更深层次。

答案 2 :(得分:0)

如果您无法更换删除@的密钥,则可以使用另一种方法进行测试 它可能有点激烈,但您可以尝试子类化NSDictionary来替换-(id)valueForKeyPath:(NSString *)keyPath方法的实现。

我说它很激烈,因为继承类集群(如NSDictionary)并不容易,你必须做很多测试才能确保RestKit不会抱怨这个。

此外,如果RestKit在一个&#34; custom&#34;中复制字典,这将不会起作用。方式(例如,从零开始构建一个全新的NSDictionary,而不是在我们的自定义子类上使用副本)。

目标是拥有一个实现类似

的方法的子类
-(id)valueForKeyPath:(NSString *)keyPath
{
    if ([[keyPath substringToIndex:1] isEqualToString:@"@"])
    {
        return [_subDictionary objectForKey:keyPath];   // if the first character is @, return the result of objectForKey
    } else {
        return [_subDictionary valueForKeyPath:keyPath];
    }

}

您可以更改方法以仅返回objectForKey的值,而不是仅在某些情况下返回valueForKeyPath的值(如果键是&#34; @ hasChildren&#34;例如)

这会给你这个结果:

self.aDictionary = [[FLDictionary alloc] initWithObjects:@[@"pluto"] forKeys:@[@"@pippo"]];
NSLog(@"Value is %@", [self valueForKeyPath:@"aDictionary.@pippo"]);


NSLog result: 2014-06-18 19:24:34.605 Swift123[25663:454958] Value is pluto

然而,现在是困难的部分。要继承NSDictionary(类集群),您必须覆盖这些方法:

-(instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt;
-(NSUInteger)count;
-(id)objectForKey:(id)aKey;
-(NSEnumerator *)keyEnumerator;

覆盖意味着您应该为新创建的字典实现自己的存储。为了避免这种情况,我创建了一个带有底层NSDictionary的实例变量。 我会在帖子的末尾发布完整的代码。

需要注意的另一件事是:它是复制NSDictionaries的惯例。 这将导致您的自定义类在第一个副本的标准NSDictionary中转换。 因此,您甚至可以继承copyWithZone:方法。

这是一个完整的工作示例:

//
//  FLDictionary.m
//  Swift123
//
//  Created by Lombardo on 18/06/14.
//  Copyright (c) 2014 Lombardo. All rights reserved.
//

import&#34; FLDictionary.h&#34;

@interface FLDictionary ()

@property (copy, nonatomic) NSDictionary *subDictionary;

@end

@implementation FLDictionary


-(instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt
{
    self = [self init];
    if (self) {
        _subDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys count:cnt];
    }
    return self;
}

-(NSUInteger)count
{
    return [_subDictionary count];
}

-(id)objectForKey:(id)aKey
{
    return [_subDictionary objectForKey:aKey];
}

-(NSEnumerator *)keyEnumerator
{
    return [_subDictionary keyEnumerator];
}

-(id)valueForKeyPath:(NSString *)keyPath
{
    if ([[keyPath substringToIndex:1] isEqualToString:@"@"])
    {
        return [_subDictionary objectForKey:keyPath];
    } else {
        return [_subDictionary valueForKeyPath:keyPath];
    }

}



-(id)copyWithZone:(NSZone *)zone
{
    id objects[ [_subDictionary count] ];
    id keys[ [_subDictionary count] ];

    int i = 0;
    for (id item in [_subDictionary allValues])
    {
        objects[i++] = item;
    }

    i = 0;
    for (id item in [_subDictionary allKeys])
    {
        keys[i++] = item;
    }

    FLDictionary *dict = [[[self class] allocWithZone:zone] initWithObjects:objects forKeys:keys count:[_subDictionary count]];
    return dict;

}

@end

答案 3 :(得分:0)

感谢您的贡献。在摆动了几个小时后,我跟着@holey和@LombaX建议来取代结果。一个简单而精彩的方法!

我最后通过简单地调用替换即<。p>来替换收到的JSON

NSString *responseStringer = operation.HTTPRequestOperation.responseString;
responseStringer = [responseStringer stringByReplacingOccurrencesOfString:@"\"@" withString:@"\""];
NSData *data = [responseStringer dataUsingEncoding:NSUTF8StringEncoding];
NSError* error;
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:@"application/json" error:&error];

将结果(parsedData)再次传递给RestKit进行映射:

NSDictionary *mappingsDictionary = @{ @"jobCategories.category": MY_RKObjectMapping_MAPPING_OBJECT };
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError) { ... }

非常感谢你的帮助,并指出了我正确的方向!