BTJSON.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #if __has_include(<Braintree/BraintreeCore.h>)
  2. #import <Braintree/BTJSON.h>
  3. #else
  4. #import <BraintreeCore/BTJSON.h>
  5. #endif
  6. NSString * const BTJSONErrorDomain = @"com.briantreepayments.BTJSONErrorDomain";
  7. @interface BTJSON ()
  8. @property (nonatomic, strong) NSArray *subscripts;
  9. @property (nonatomic, strong) id value;
  10. @end
  11. @implementation BTJSON
  12. @synthesize value = _value;
  13. - (instancetype)init {
  14. self = [super init];
  15. if (self) {
  16. self.subscripts = [NSMutableArray array];
  17. self.value = @{};
  18. }
  19. return self;
  20. }
  21. - (instancetype)initWithData:(NSData *)data {
  22. NSError *error;
  23. id value = [NSJSONSerialization JSONObjectWithData:data
  24. options:NSJSONReadingAllowFragments
  25. error:&error];
  26. if (error != nil) {
  27. return [self initWithValue:error];
  28. }
  29. return [self initWithValue:value];
  30. }
  31. - (instancetype)initWithValue:(id)value {
  32. self = [self init];
  33. if (self) {
  34. self.value = value;
  35. }
  36. return self;
  37. }
  38. #pragma mark Subscripting
  39. - (BTJSON *)objectForKeyedSubscript:(NSString *)key {
  40. BTJSON *json = [[BTJSON alloc] initWithValue:_value];
  41. json.subscripts = [self.subscripts arrayByAddingObject:key];
  42. return json;
  43. }
  44. - (BTJSON *)objectAtIndexedSubscript:(NSUInteger)idx {
  45. BTJSON *json = [[BTJSON alloc] initWithValue:_value];
  46. json.subscripts = [self.subscripts arrayByAddingObject:@(idx)];
  47. return json;
  48. }
  49. - (id)value {
  50. id value = _value;
  51. for (id key in self.subscripts) {
  52. if ([value isKindOfClass:[NSArray class]]) {
  53. if (![key isKindOfClass:[NSNumber class]]) {
  54. value = [self chainedErrorOrErrorWithCode:BTJSONErrorAccessInvalid userInfo:nil];
  55. break;
  56. }
  57. NSUInteger idx = [(NSNumber *)key unsignedIntegerValue];
  58. if (idx >= [(NSArray *)value count]) {
  59. value = nil;
  60. break;
  61. }
  62. value = [value objectAtIndexedSubscript:idx];
  63. } else if ([value isKindOfClass:[NSDictionary class]]) {
  64. if (![key isKindOfClass:[NSString class]]) {
  65. value = [self chainedErrorOrErrorWithCode:BTJSONErrorAccessInvalid userInfo:nil];
  66. break;
  67. }
  68. value = [value objectForKeyedSubscript:key];
  69. } else {
  70. 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] }];
  71. break;
  72. }
  73. }
  74. return value;
  75. }
  76. #pragma mark Validity Checks
  77. - (BOOL)isError {
  78. return [self.value isKindOfClass:[NSError class]];
  79. }
  80. - (NSError *)asError {
  81. if (![self.value isKindOfClass:[NSError class]]) {
  82. return nil;
  83. }
  84. return self.value;
  85. }
  86. #pragma mark Generating JSON
  87. - (NSData *)asJSONAndReturnError:(NSError **)error {
  88. return [NSJSONSerialization dataWithJSONObject:self.value
  89. options:0
  90. error:error];
  91. }
  92. - (NSString *)asPrettyJSONAndReturnError:(NSError **)error {
  93. return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:self.value
  94. options:NSJSONWritingPrettyPrinted
  95. error:error]
  96. encoding:NSUTF8StringEncoding];
  97. }
  98. #pragma mark JSON Type Casts
  99. - (NSString *)asString {
  100. if (![self.value isKindOfClass:[NSString class]]) {
  101. return nil;
  102. }
  103. return self.value;
  104. }
  105. - (NSArray<BTJSON *> *)asArray {
  106. if (![self.value isKindOfClass:[NSArray class]]) {
  107. return nil;
  108. }
  109. NSMutableArray *array = [NSMutableArray new];
  110. for (id element in self.value) {
  111. [array addObject:[[BTJSON alloc] initWithValue:element]];
  112. }
  113. return array;
  114. }
  115. - (NSDecimalNumber *)asNumber {
  116. if (![self.value isKindOfClass:[NSNumber class]]) {
  117. return nil;
  118. }
  119. return [NSDecimalNumber decimalNumberWithDecimal:[self.value decimalValue]];
  120. }
  121. #pragma mark JSON Extension Type Casts
  122. - (NSURL *)asURL {
  123. NSString *urlString = self.asString;
  124. if (urlString == nil) {
  125. return nil;
  126. }
  127. return [NSURL URLWithString:urlString];
  128. }
  129. - (NSArray<NSString *> *)asStringArray {
  130. if (![self.value isKindOfClass:[NSArray class]]) {
  131. return nil;
  132. }
  133. for (id obj in self.value) {
  134. if (![obj isKindOfClass:[NSString class]]) {
  135. return nil;
  136. }
  137. }
  138. return self.value;
  139. }
  140. - (NSDictionary<NSString *, BTJSON *> *)asDictionary {
  141. NSDictionary *dictionary = self.value;
  142. if (![dictionary isKindOfClass:[NSDictionary class]]) {
  143. return nil;
  144. }
  145. return dictionary;
  146. }
  147. - (NSInteger)asIntegerOrZero {
  148. NSNumber *number = self.value;
  149. if (![number isKindOfClass:[NSNumber class]]) {
  150. return 0;
  151. }
  152. return number.integerValue;
  153. }
  154. - (NSInteger)asEnum:(nonnull NSDictionary *)mapping orDefault:(NSInteger)defaultValue {
  155. id key = self.value;
  156. NSNumber *value = mapping[key];
  157. if (value == nil || ![value isKindOfClass:[NSNumber class]]) {
  158. return defaultValue;
  159. }
  160. return value.integerValue;
  161. }
  162. // @name JSON Type Checks
  163. - (BOOL)isString {
  164. return [self.value isKindOfClass:[NSString class]];
  165. }
  166. - (BOOL)isNumber {
  167. return [self.value isKindOfClass:[NSNumber class]];
  168. }
  169. - (BOOL)isArray {
  170. return [self.value isKindOfClass:[NSArray class]];
  171. }
  172. - (BOOL)isObject {
  173. return [self.value isKindOfClass:[NSDictionary class]];
  174. }
  175. - (BOOL)isBool {
  176. return [self.value isEqual:@YES] || [self.value isEqual:@NO];
  177. }
  178. - (BOOL)isTrue {
  179. return [self.value isEqual:@YES];
  180. }
  181. - (BOOL)isFalse {
  182. return [self.value isEqual:@NO];
  183. }
  184. - (BOOL)isNull {
  185. return [self.value isKindOfClass:[NSNull class]];
  186. }
  187. #pragma mark Error Handling
  188. - (NSError *)chainedErrorOrErrorWithCode:(NSInteger)code
  189. userInfo:(NSDictionary *)userInfo {
  190. if ([_value isKindOfClass:[NSError class]]) {
  191. return _value;
  192. }
  193. return [NSError errorWithDomain:BTJSONErrorDomain
  194. code:code
  195. userInfo:userInfo];
  196. }
  197. #pragma mark -
  198. - (NSString *)description {
  199. return [self debugDescription];
  200. }
  201. - (NSString *)debugDescription {
  202. return [NSString stringWithFormat:@"<BTJSON:%p value:%@>", self, self.value];
  203. }
  204. @end