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.
393 lines
12 KiB
393 lines
12 KiB
![]()
2 years ago
|
//
|
||
|
// 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
|