New file |
| | |
| | | // |
| | | // NSDictionary+YYAdd.m |
| | | // YYCategories <https://github.com/ibireme/YYCategories> |
| | | // |
| | | // 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 "YYCategoriesMacro.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]]) { |
| | | parent[[parent 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; |
| | | } |
| | | |
| | | - (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; |
| | | } |
| | | |
| | | - (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]; |
| | | 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]; |
| | | 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; |
| | | } |
| | | |
| | | @end |