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

//
// 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