You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
12 KiB
392 lines
12 KiB
// |
|
// NSDictionary+YYAdd.m |
|
// YYKit <https://github.com/ibireme/YYKit> |
|
// |
|
// Created by ibireme on 13/4/4. |
|
// Copyright (c) 2015 ibireme. |
|
// |
|
// This source code is licensed under the MIT-style license found in the |
|
// LICENSE file in the root directory of this source tree. |
|
// |
|
|
|
#import "NSDictionary+YYAdd.h" |
|
#import "NSString+YYAdd.h" |
|
#import "NSData+YYAdd.h" |
|
#import "YYKitMacro.h" |
|
|
|
YYSYNTH_DUMMY_CLASS(NSDictionary_YYAdd) |
|
|
|
|
|
@interface _YYXMLDictionaryParser : NSObject <NSXMLParserDelegate> |
|
@end |
|
|
|
@implementation _YYXMLDictionaryParser { |
|
NSMutableDictionary *_root; |
|
NSMutableArray *_stack; |
|
NSMutableString *_text; |
|
} |
|
|
|
- (instancetype)initWithData:(NSData *)data { |
|
self = super.init; |
|
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; |
|
[parser setDelegate:self]; |
|
[parser parse]; |
|
return self; |
|
} |
|
|
|
- (instancetype)initWithString:(NSString *)xml { |
|
NSData *data = [xml dataUsingEncoding:NSUTF8StringEncoding]; |
|
return [self initWithData:data]; |
|
} |
|
|
|
- (NSDictionary *)result { |
|
return _root; |
|
} |
|
|
|
#pragma mark - NSXMLParserDelegate |
|
|
|
#define XMLText @"_text" |
|
#define XMLName @"_name" |
|
#define XMLPref @"_" |
|
|
|
- (void)textEnd { |
|
_text = _text.stringByTrim.mutableCopy; |
|
if (_text.length) { |
|
NSMutableDictionary *top = _stack.lastObject; |
|
id existing = top[XMLText]; |
|
if ([existing isKindOfClass:[NSArray class]]) { |
|
[existing addObject:_text]; |
|
} else if (existing) { |
|
top[XMLText] = [@[existing, _text] mutableCopy]; |
|
} else { |
|
top[XMLText] = _text; |
|
} |
|
} |
|
_text = nil; |
|
} |
|
|
|
- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict { |
|
[self textEnd]; |
|
|
|
NSMutableDictionary *node = [NSMutableDictionary new]; |
|
if (!_root) node[XMLName] = elementName; |
|
if (attributeDict.count) [node addEntriesFromDictionary:attributeDict]; |
|
|
|
if (_root) { |
|
NSMutableDictionary *top = _stack.lastObject; |
|
id existing = top[elementName]; |
|
if ([existing isKindOfClass:[NSArray class]]) { |
|
[existing addObject:node]; |
|
} else if (existing) { |
|
top[elementName] = [@[existing, node] mutableCopy]; |
|
} else { |
|
top[elementName] = node; |
|
} |
|
[_stack addObject:node]; |
|
} else { |
|
_root = node; |
|
_stack = [NSMutableArray arrayWithObject:node]; |
|
} |
|
} |
|
|
|
- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName { |
|
[self textEnd]; |
|
|
|
NSMutableDictionary *top = _stack.lastObject; |
|
[_stack removeLastObject]; |
|
|
|
NSMutableDictionary *left = top.mutableCopy; |
|
[left removeObjectsForKeys:@[XMLText, XMLName]]; |
|
for (NSString *key in left.allKeys) { |
|
[left removeObjectForKey:key]; |
|
if ([key hasPrefix:XMLPref]) { |
|
left[[key substringFromIndex:XMLPref.length]] = top[key]; |
|
} |
|
} |
|
if (left.count) return; |
|
|
|
NSMutableDictionary *children = top.mutableCopy; |
|
[children removeObjectsForKeys:@[XMLText, XMLName]]; |
|
for (NSString *key in children.allKeys) { |
|
if ([key hasPrefix:XMLPref]) { |
|
[children removeObjectForKey:key]; |
|
} |
|
} |
|
if (children.count) return; |
|
|
|
NSMutableDictionary *topNew = _stack.lastObject; |
|
NSString *nodeName = top[XMLName]; |
|
if (!nodeName) { |
|
for (NSString *name in topNew) { |
|
id object = topNew[name]; |
|
if (object == top) { |
|
nodeName = name; break; |
|
} else if ([object isKindOfClass:[NSArray class]] && [object containsObject:top]) { |
|
nodeName = name; break; |
|
} |
|
} |
|
} |
|
if (!nodeName) return; |
|
|
|
id inner = top[XMLText]; |
|
if ([inner isKindOfClass:[NSArray class]]) { |
|
inner = [inner componentsJoinedByString:@"\n"]; |
|
} |
|
if (!inner) return; |
|
|
|
id parent = topNew[nodeName]; |
|
if ([parent isKindOfClass:[NSArray class]]) { |
|
NSArray *parentAsArray = parent; |
|
parent[parentAsArray.count - 1] = inner; |
|
} else { |
|
topNew[nodeName] = inner; |
|
} |
|
} |
|
|
|
- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string { |
|
if (_text) [_text appendString:string]; |
|
else _text = [NSMutableString stringWithString:string]; |
|
} |
|
|
|
- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock { |
|
NSString *string = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]; |
|
if (_text) [_text appendString:string]; |
|
else _text = [NSMutableString stringWithString:string]; |
|
} |
|
|
|
#undef XMLText |
|
#undef XMLName |
|
#undef XMLPref |
|
@end |
|
|
|
|
|
@implementation NSDictionary (YYAdd) |
|
|
|
+ (NSDictionary *)dictionaryWithPlistData:(NSData *)plist { |
|
if (!plist) return nil; |
|
NSDictionary *dictionary = [NSPropertyListSerialization propertyListWithData:plist options:NSPropertyListImmutable format:NULL error:NULL]; |
|
if ([dictionary isKindOfClass:[NSDictionary class]]) return dictionary; |
|
return nil; |
|
} |
|
|
|
+ (NSDictionary *)dictionaryWithPlistString:(NSString *)plist { |
|
if (!plist) return nil; |
|
NSData *data = [plist dataUsingEncoding:NSUTF8StringEncoding]; |
|
return [self dictionaryWithPlistData:data]; |
|
} |
|
|
|
- (NSData *)plistData { |
|
return [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:kNilOptions error:NULL]; |
|
} |
|
|
|
- (NSString *)plistString { |
|
NSData *xmlData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListXMLFormat_v1_0 options:kNilOptions error:NULL]; |
|
if (xmlData) return xmlData.utf8String; |
|
return nil; |
|
} |
|
|
|
- (NSArray *)allKeysSorted { |
|
return [[self allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; |
|
} |
|
|
|
- (NSArray *)allValuesSortedByKeys { |
|
NSArray *sortedKeys = [self allKeysSorted]; |
|
NSMutableArray *arr = [[NSMutableArray alloc] init]; |
|
for (id key in sortedKeys) { |
|
[arr addObject:self[key]]; |
|
} |
|
return [arr copy]; |
|
} |
|
|
|
- (BOOL)containsObjectForKey:(id)key { |
|
if (!key) return NO; |
|
return self[key] != nil; |
|
} |
|
|
|
- (NSDictionary *)entriesForKeys:(NSArray *)keys { |
|
NSMutableDictionary *dic = [NSMutableDictionary new]; |
|
for (id key in keys) { |
|
id value = self[key]; |
|
if (value) dic[key] = value; |
|
} |
|
return [dic copy]; |
|
} |
|
|
|
- (NSString *)jsonStringEncoded { |
|
if ([NSJSONSerialization isValidJSONObject:self]) { |
|
NSError *error; |
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error]; |
|
NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; |
|
if (!error) return json; |
|
} |
|
return nil; |
|
} |
|
|
|
- (NSString *)jsonPrettyStringEncoded { |
|
if ([NSJSONSerialization isValidJSONObject:self]) { |
|
NSError *error; |
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error]; |
|
NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; |
|
if (!error) return json; |
|
} |
|
return nil; |
|
} |
|
|
|
+ (NSDictionary *)dictionaryWithXML:(id)xml { |
|
_YYXMLDictionaryParser *parser = nil; |
|
if ([xml isKindOfClass:[NSString class]]) { |
|
parser = [[_YYXMLDictionaryParser alloc] initWithString:xml]; |
|
} else if ([xml isKindOfClass:[NSData class]]) { |
|
parser = [[_YYXMLDictionaryParser alloc] initWithData:xml]; |
|
} |
|
return [parser result]; |
|
} |
|
|
|
|
|
/// Get a number value from 'id'. |
|
static NSNumber *NSNumberFromID(id value) { |
|
static NSCharacterSet *dot; |
|
static dispatch_once_t onceToken; |
|
dispatch_once(&onceToken, ^{ |
|
dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)]; |
|
}); |
|
if (!value || value == [NSNull null]) return nil; |
|
if ([value isKindOfClass:[NSNumber class]]) return value; |
|
if ([value isKindOfClass:[NSString class]]) { |
|
NSString *lower = ((NSString *)value).lowercaseString; |
|
if ([lower isEqualToString:@"true"] || [lower isEqualToString:@"yes"]) return @(YES); |
|
if ([lower isEqualToString:@"false"] || [lower isEqualToString:@"no"]) return @(NO); |
|
if ([lower isEqualToString:@"nil"] || [lower isEqualToString:@"null"]) return nil; |
|
if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) { |
|
return @(((NSString *)value).doubleValue); |
|
} else { |
|
return @(((NSString *)value).longLongValue); |
|
} |
|
} |
|
return nil; |
|
} |
|
|
|
#define RETURN_VALUE(_type_) \ |
|
if (!key) return def; \ |
|
id value = self[key]; \ |
|
if (!value || value == [NSNull null]) return def; \ |
|
if ([value isKindOfClass:[NSNumber class]]) return ((NSNumber *)value)._type_; \ |
|
if ([value isKindOfClass:[NSString class]]) return NSNumberFromID(value)._type_; \ |
|
return def; |
|
|
|
- (BOOL)boolValueForKey:(NSString *)key default:(BOOL)def { |
|
RETURN_VALUE(boolValue); |
|
} |
|
|
|
- (char)charValueForKey:(NSString *)key default:(char)def { |
|
RETURN_VALUE(charValue); |
|
} |
|
|
|
- (unsigned char)unsignedCharValueForKey:(NSString *)key default:(unsigned char)def { |
|
RETURN_VALUE(unsignedCharValue); |
|
} |
|
|
|
- (short)shortValueForKey:(NSString *)key default:(short)def { |
|
RETURN_VALUE(shortValue); |
|
} |
|
|
|
- (unsigned short)unsignedShortValueForKey:(NSString *)key default:(unsigned short)def { |
|
RETURN_VALUE(unsignedShortValue); |
|
} |
|
|
|
- (int)intValueForKey:(NSString *)key default:(int)def { |
|
RETURN_VALUE(intValue); |
|
} |
|
|
|
- (unsigned int)unsignedIntValueForKey:(NSString *)key default:(unsigned int)def { |
|
RETURN_VALUE(unsignedIntValue); |
|
} |
|
|
|
- (long)longValueForKey:(NSString *)key default:(long)def { |
|
RETURN_VALUE(longValue); |
|
} |
|
|
|
- (unsigned long)unsignedLongValueForKey:(NSString *)key default:(unsigned long)def { |
|
RETURN_VALUE(unsignedLongValue); |
|
} |
|
|
|
- (long long)longLongValueForKey:(NSString *)key default:(long long)def { |
|
RETURN_VALUE(longLongValue); |
|
} |
|
|
|
- (unsigned long long)unsignedLongLongValueForKey:(NSString *)key default:(unsigned long long)def { |
|
RETURN_VALUE(unsignedLongLongValue); |
|
} |
|
|
|
- (float)floatValueForKey:(NSString *)key default:(float)def { |
|
RETURN_VALUE(floatValue); |
|
} |
|
|
|
- (double)doubleValueForKey:(NSString *)key default:(double)def { |
|
RETURN_VALUE(doubleValue); |
|
} |
|
|
|
- (NSInteger)integerValueForKey:(NSString *)key default:(NSInteger)def { |
|
RETURN_VALUE(integerValue); |
|
} |
|
|
|
- (NSUInteger)unsignedIntegerValueForKey:(NSString *)key default:(NSUInteger)def { |
|
RETURN_VALUE(unsignedIntegerValue); |
|
} |
|
|
|
- (NSNumber *)numberValueForKey:(NSString *)key default:(NSNumber *)def { |
|
if (!key) return def; |
|
id value = self[key]; |
|
if (!value || value == [NSNull null]) return def; |
|
if ([value isKindOfClass:[NSNumber class]]) return value; |
|
if ([value isKindOfClass:[NSString class]]) return NSNumberFromID(value); |
|
return def; |
|
} |
|
|
|
- (NSString *)stringValueForKey:(NSString *)key default:(NSString *)def { |
|
if (!key) return def; |
|
id value = self[key]; |
|
if (!value || value == [NSNull null]) return def; |
|
if ([value isKindOfClass:[NSString class]]) return value; |
|
if ([value isKindOfClass:[NSNumber class]]) return ((NSNumber *)value).description; |
|
return def; |
|
} |
|
|
|
@end |
|
|
|
|
|
@implementation NSMutableDictionary (YYAdd) |
|
|
|
+ (NSMutableDictionary *)dictionaryWithPlistData:(NSData *)plist { |
|
if (!plist) return nil; |
|
NSMutableDictionary *dictionary = [NSPropertyListSerialization propertyListWithData:plist options:NSPropertyListMutableContainersAndLeaves format:NULL error:NULL]; |
|
if ([dictionary isKindOfClass:[NSMutableDictionary class]]) return dictionary; |
|
return nil; |
|
} |
|
|
|
+ (NSMutableDictionary *)dictionaryWithPlistString:(NSString *)plist { |
|
if (!plist) return nil; |
|
NSData *data = [plist dataUsingEncoding:NSUTF8StringEncoding]; |
|
return [self dictionaryWithPlistData:data]; |
|
} |
|
|
|
- (id)popObjectForKey:(id)aKey { |
|
if (!aKey) return nil; |
|
id value = self[aKey]; |
|
[self removeObjectForKey:aKey]; |
|
return value; |
|
} |
|
|
|
- (NSDictionary *)popEntriesForKeys:(NSArray *)keys { |
|
NSMutableDictionary *dic = [NSMutableDictionary new]; |
|
for (id key in keys) { |
|
id value = self[key]; |
|
if (value) { |
|
[self removeObjectForKey:key]; |
|
dic[key] = value; |
|
} |
|
} |
|
return [dic copy]; |
|
} |
|
|
|
@end
|
|
|