| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- #if __has_include(<Braintree/BraintreeCore.h>)
- #import <Braintree/BTJSON.h>
- #else
- #import <BraintreeCore/BTJSON.h>
- #endif
- NSString * const BTJSONErrorDomain = @"com.briantreepayments.BTJSONErrorDomain";
- @interface BTJSON ()
- @property (nonatomic, strong) NSArray *subscripts;
- @property (nonatomic, strong) id value;
- @end
- @implementation BTJSON
- @synthesize value = _value;
- - (instancetype)init {
- self = [super init];
- if (self) {
- self.subscripts = [NSMutableArray array];
- self.value = @{};
- }
- return self;
- }
- - (instancetype)initWithData:(NSData *)data {
- NSError *error;
- id value = [NSJSONSerialization JSONObjectWithData:data
- options:NSJSONReadingAllowFragments
- error:&error];
- if (error != nil) {
- return [self initWithValue:error];
- }
- return [self initWithValue:value];
- }
- - (instancetype)initWithValue:(id)value {
- self = [self init];
- if (self) {
- self.value = value;
- }
- return self;
- }
- #pragma mark Subscripting
- - (BTJSON *)objectForKeyedSubscript:(NSString *)key {
- BTJSON *json = [[BTJSON alloc] initWithValue:_value];
- json.subscripts = [self.subscripts arrayByAddingObject:key];
- return json;
- }
- - (BTJSON *)objectAtIndexedSubscript:(NSUInteger)idx {
- BTJSON *json = [[BTJSON alloc] initWithValue:_value];
- json.subscripts = [self.subscripts arrayByAddingObject:@(idx)];
- return json;
- }
- - (id)value {
- id value = _value;
- for (id key in self.subscripts) {
- if ([value isKindOfClass:[NSArray class]]) {
- if (![key isKindOfClass:[NSNumber class]]) {
- value = [self chainedErrorOrErrorWithCode:BTJSONErrorAccessInvalid userInfo:nil];
- break;
- }
- NSUInteger idx = [(NSNumber *)key unsignedIntegerValue];
- if (idx >= [(NSArray *)value count]) {
- value = nil;
- break;
- }
- value = [value objectAtIndexedSubscript:idx];
- } else if ([value isKindOfClass:[NSDictionary class]]) {
- if (![key isKindOfClass:[NSString class]]) {
- value = [self chainedErrorOrErrorWithCode:BTJSONErrorAccessInvalid userInfo:nil];
- break;
- }
- value = [value objectForKeyedSubscript:key];
- } else {
- value = [self chainedErrorOrErrorWithCode:BTJSONErrorValueInvalid userInfo:@{ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to index into a value that is neither an object nor an array using key (%@).", key] }];
- break;
- }
- }
- return value;
- }
- #pragma mark Validity Checks
- - (BOOL)isError {
- return [self.value isKindOfClass:[NSError class]];
- }
- - (NSError *)asError {
- if (![self.value isKindOfClass:[NSError class]]) {
- return nil;
- }
- return self.value;
- }
- #pragma mark Generating JSON
- - (NSData *)asJSONAndReturnError:(NSError **)error {
- return [NSJSONSerialization dataWithJSONObject:self.value
- options:0
- error:error];
- }
- - (NSString *)asPrettyJSONAndReturnError:(NSError **)error {
- return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:self.value
- options:NSJSONWritingPrettyPrinted
- error:error]
- encoding:NSUTF8StringEncoding];
- }
- #pragma mark JSON Type Casts
- - (NSString *)asString {
- if (![self.value isKindOfClass:[NSString class]]) {
- return nil;
- }
- return self.value;
- }
- - (NSArray<BTJSON *> *)asArray {
- if (![self.value isKindOfClass:[NSArray class]]) {
- return nil;
- }
- NSMutableArray *array = [NSMutableArray new];
- for (id element in self.value) {
- [array addObject:[[BTJSON alloc] initWithValue:element]];
- }
- return array;
- }
- - (NSDecimalNumber *)asNumber {
- if (![self.value isKindOfClass:[NSNumber class]]) {
- return nil;
- }
- return [NSDecimalNumber decimalNumberWithDecimal:[self.value decimalValue]];
- }
- #pragma mark JSON Extension Type Casts
- - (NSURL *)asURL {
- NSString *urlString = self.asString;
- if (urlString == nil) {
- return nil;
- }
- return [NSURL URLWithString:urlString];
- }
- - (NSArray<NSString *> *)asStringArray {
- if (![self.value isKindOfClass:[NSArray class]]) {
- return nil;
- }
- for (id obj in self.value) {
- if (![obj isKindOfClass:[NSString class]]) {
- return nil;
- }
- }
- return self.value;
- }
- - (NSDictionary<NSString *, BTJSON *> *)asDictionary {
- NSDictionary *dictionary = self.value;
- if (![dictionary isKindOfClass:[NSDictionary class]]) {
- return nil;
- }
- return dictionary;
- }
- - (NSInteger)asIntegerOrZero {
- NSNumber *number = self.value;
- if (![number isKindOfClass:[NSNumber class]]) {
- return 0;
- }
- return number.integerValue;
- }
- - (NSInteger)asEnum:(nonnull NSDictionary *)mapping orDefault:(NSInteger)defaultValue {
- id key = self.value;
- NSNumber *value = mapping[key];
- if (value == nil || ![value isKindOfClass:[NSNumber class]]) {
- return defaultValue;
- }
- return value.integerValue;
- }
- // @name JSON Type Checks
- - (BOOL)isString {
- return [self.value isKindOfClass:[NSString class]];
- }
- - (BOOL)isNumber {
- return [self.value isKindOfClass:[NSNumber class]];
- }
- - (BOOL)isArray {
- return [self.value isKindOfClass:[NSArray class]];
- }
- - (BOOL)isObject {
- return [self.value isKindOfClass:[NSDictionary class]];
- }
- - (BOOL)isBool {
- return [self.value isEqual:@YES] || [self.value isEqual:@NO];
- }
- - (BOOL)isTrue {
- return [self.value isEqual:@YES];
- }
- - (BOOL)isFalse {
- return [self.value isEqual:@NO];
- }
- - (BOOL)isNull {
- return [self.value isKindOfClass:[NSNull class]];
- }
- #pragma mark Error Handling
- - (NSError *)chainedErrorOrErrorWithCode:(NSInteger)code
- userInfo:(NSDictionary *)userInfo {
- if ([_value isKindOfClass:[NSError class]]) {
- return _value;
- }
- return [NSError errorWithDomain:BTJSONErrorDomain
- code:code
- userInfo:userInfo];
- }
- #pragma mark -
- - (NSString *)description {
- return [self debugDescription];
- }
- - (NSString *)debugDescription {
- return [NSString stringWithFormat:@"<BTJSON:%p value:%@>", self, self.value];
- }
- @end
|