我将 JSON 对象存储在数据库中。这些对象中的许多(也许是大多数)都是重复的,因此我想将它们键入 SHA 散列之类的对象,以避免创建不必要的额外记录。
问题是,在我想将它们写入数据库时,我不再拥有 JSON 字节——只有 NSJSONSerialization
返回的 Foundation 对象。因为 NSDictionary
不对键顺序做任何保证(即使它做了,我也不确定我从中获取数据的服务器),我不能确定 NSJSONSerialization
会在我每次调用它时以相同的顺序输出每个对象的字段。这意味着同一个对象可能有不同的摘要,这让我节省空间的尝试落空了。
是否有一个 Objective-C JSON 库确实总是为等效对象编写完全相同的 JSON,大概是在编写键之前对键进行排序?我的目标是 iOS 7,但这可能是基础级别的问题。
最佳答案
我没有尝试编写自己的 JSON 序列化程序,而是决定通过一些代理技巧来欺骗 Apple 做我想做的事情。
用法:
NSData * JSONData = [NSJSONSerialization dataWithJSONObject:[jsonObject objectWithSortedKeys] options:0 error:&error];
标题:
#import <Foundation/Foundation.h>
@interface NSObject (sortedKeys)
/// Returns a proxy for the object in which all dictionary keys, including those of child objects at any level, will always be enumerated in sorted order.
- (id)objectWithSortedKeys;
@end
代码:
#import "NSObject+sortedKeys.h"
/// A CbxSortedKeyWrapper intercepts calls to methods like -allKeys, -objectEnumerator, -enumerateKeysAndObjectsUsingBlock:, etc. and makes them enumerate a sorted array of keys, thus ensuring that keys are enumerated in a stable order. It also replaces objects returned by any other methods (including, say, -objectForKey: or -objectAtIndex:) with wrapped versions of those objects, thereby ensuring that child objects are similarly sorted. There are a lot of flaws in this approach, but it works well enough for NSJSONSerialization.
@interface CbxSortedKeyWrapper: NSProxy
+ (id)sortedKeyWrapperForObject:(id)object;
@end
@implementation NSObject (sortedKeys)
- (id)objectWithSortedKeys {
return [CbxSortedKeyWrapper sortedKeyWrapperForObject:self];
}
@end
@implementation CbxSortedKeyWrapper {
id _representedObject;
NSArray * _keys;
}
+ (id)sortedKeyWrapperForObject:(id)object {
if(!object) {
return nil;
}
CbxSortedKeyWrapper * wrapper = [self alloc];
wrapper->_representedObject = [object copy];
if([wrapper->_representedObject respondsToSelector:@selector(allKeys)]) {
wrapper->_keys = [[wrapper->_representedObject allKeys] sortedArrayUsingSelector:@selector(compare:)];
}
return wrapper;
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector {
return [_representedObject methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation*)invocation {
[invocation invokeWithTarget:_representedObject];
BOOL returnsObject = invocation.methodSignature.methodReturnType[0] == '@';
if(returnsObject) {
__unsafe_unretained id out = nil;
[invocation getReturnValue:&out];
__unsafe_unretained id wrapper = [CbxSortedKeyWrapper sortedKeyWrapperForObject:out];
[invocation setReturnValue:&wrapper];
}
}
- (NSEnumerator *)keyEnumerator {
return [_keys objectEnumerator];
}
- (NSEnumerator *)objectEnumerator {
if(_keys) {
return [[self allValues] objectEnumerator];
}
else {
return [CbxSortedKeyWrapper sortedKeyWrapperForObject:[_representedObject objectEnumerator]];
}
}
- (NSArray *)allKeys {
return _keys;
}
- (NSArray *)allValues {
return [CbxSortedKeyWrapper sortedKeyWrapperForObject:[_representedObject objectsForKeys:_keys notFoundMarker:[NSNull null]]];
}
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block {
[_keys enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) {
id obj = [CbxSortedKeyWrapper sortedKeyWrapperForObject:_representedObject[key]];
block(key, obj, stop);
}];
}
- (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id key, id obj, BOOL *stop))block {
[_keys enumerateObjectsWithOptions:opts usingBlock:^(id key, NSUInteger idx, BOOL *stop) {
id obj = [CbxSortedKeyWrapper sortedKeyWrapperForObject:_representedObject[key]];
block(key, obj, stop);
}];
}
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
[_representedObject enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
block([CbxSortedKeyWrapper sortedKeyWrapperForObject:obj], idx, stop);
}];
}
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
[_representedObject enumerateObjectsWithOptions:opts usingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
block([CbxSortedKeyWrapper sortedKeyWrapperForObject:obj], idx, stop);
}];
}
- (void)enumerateObjectsAtIndexes:(NSIndexSet *)indexSet options:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
[_representedObject enumerateObjectsAtIndexes:indexSet options:opts usingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
block([CbxSortedKeyWrapper sortedKeyWrapperForObject:obj], idx, stop);
}];
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {
NSUInteger count = [_keys countByEnumeratingWithState:state objects:stackbuf count:len];
for(NSUInteger i = 0; i < count; i++) {
stackbuf[i] = [CbxSortedKeyWrapper sortedKeyWrapperForObject:stackbuf[i]];
}
return count;
}
@end
关于ios - 规范化 JSON,使等效对象具有相同的哈希值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21672923/