JSONModel.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. //
  2. // JSONModel.h
  3. //
  4. // @version 1.2
  5. // @author Marin Todorov (http://www.underplot.com) and contributors
  6. //
  7. // Copyright (c) 2012-2015 Marin Todorov, Underplot ltd.
  8. // This code is distributed under the terms and conditions of the MIT license.
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  11. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  13. //
  14. #import <Foundation/Foundation.h>
  15. #import "JSONModelError.h"
  16. #import "JSONValueTransformer.h"
  17. #import "JSONKeyMapper.h"
  18. /////////////////////////////////////////////////////////////////////////////////////////////
  19. #if TARGET_IPHONE_SIMULATOR
  20. #define JMLog( s, ... ) NSLog( @"[%@:%d] %@", [[NSString stringWithUTF8String:__FILE__] \
  21. lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
  22. #else
  23. #define JMLog( s, ... )
  24. #endif
  25. /////////////////////////////////////////////////////////////////////////////////////////////
  26. #pragma mark - Property Protocols
  27. /**
  28. * Protocol for defining properties in a JSON Model class that should not be considered at all
  29. * neither while importing nor when exporting JSON.
  30. *
  31. * @property (strong, nonatomic) NSString&lt;Ignore&gt;* propertyName;
  32. *
  33. */
  34. @protocol Ignore
  35. @end
  36. /**
  37. * Protocol for defining optional properties in a JSON Model class. Use like below to define
  38. * model properties that are not required to have values in the JSON input:
  39. *
  40. * @property (strong, nonatomic) NSString&lt;Optional&gt;* propertyName;
  41. *
  42. */
  43. @protocol Optional
  44. @end
  45. /**
  46. * Protocol for defining index properties in a JSON Model class. Use like below to define
  47. * model properties that are considered the Model's identifier (id).
  48. *
  49. * @property (strong, nonatomic) NSString&lt;Index&gt;* propertyName;
  50. *
  51. */
  52. @protocol Index
  53. @end
  54. /**
  55. * Make all objects Optional compatible to avoid compiler warnings
  56. */
  57. @interface NSObject(JSONModelPropertyCompatibility)<Optional, Index, Ignore>
  58. @end
  59. /**
  60. * ConvertOnDemand enables lazy model initialization for NSArrays of models
  61. *
  62. * @property (strong, nonatomic) NSArray&lt;JSONModel, ConvertOnDemand&gt;* propertyName;
  63. */
  64. @protocol ConvertOnDemand
  65. @end
  66. /**
  67. * Make all arrays ConvertOnDemand compatible to avoid compiler warnings
  68. */
  69. @interface NSArray(JSONModelPropertyCompatibility)<ConvertOnDemand>
  70. @end
  71. /////////////////////////////////////////////////////////////////////////////////////////////
  72. #pragma mark - JSONModel protocol
  73. /**
  74. * A protocol describing an abstract JSONModel class
  75. * JSONModel conforms to this protocol, so it can use itself abstractly
  76. */
  77. @protocol AbstractJSONModelProtocol <NSCopying, NSCoding>
  78. @required
  79. /**
  80. * All JSONModel classes should implement initWithDictionary:
  81. *
  82. * For most classes the default initWithDictionary: inherited from JSONModel itself
  83. * should suffice, but developers have the option ot also overwrite it if needed.
  84. *
  85. * @param dict a dictionary holding JSON objects, to be imported in the model.
  86. * @param err an error or NULL
  87. */
  88. -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError**)err;
  89. /**
  90. * All JSONModel classes should implement initWithData:error:
  91. *
  92. * For most classes the default initWithData: inherited from JSONModel itself
  93. * should suffice, but developers have the option ot also overwrite it if needed.
  94. *
  95. * @param data representing a JSON response (usually fetched from web), to be imported in the model.
  96. * @param error an error or NULL
  97. */
  98. -(instancetype)initWithData:(NSData*)data error:(NSError**)error;
  99. /**
  100. * All JSONModel classes should be able to export themselves as a dictionary of
  101. * JSON compliant objects.
  102. *
  103. * For most classes the inherited from JSONModel default toDictionary implementation
  104. * should suffice.
  105. *
  106. * @return NSDictionary dictionary of JSON compliant objects
  107. * @exception JSONModelTypeNotAllowedException thrown when one of your model's custom class properties
  108. * does not have matching transformer method in an JSONValueTransformer.
  109. */
  110. -(NSDictionary*)toDictionary;
  111. /**
  112. * Export a model class to a dictionary, including only given properties
  113. *
  114. * @param propertyNames the properties to export; if nil, all properties exported
  115. * @return NSDictionary dictionary of JSON compliant objects
  116. * @exception JSONModelTypeNotAllowedException thrown when one of your model's custom class properties
  117. * does not have matching transformer method in an JSONValueTransformer.
  118. */
  119. -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames;
  120. @end
  121. /////////////////////////////////////////////////////////////////////////////////////////////
  122. #pragma mark - JSONModel interface
  123. /**
  124. * The JSONModel is an abstract model class, you should not instantiate it directly,
  125. * as it does not have any properties, and therefore cannot serve as a data model.
  126. * Instead you should subclass it, and define the properties you want your data model
  127. * to have as properties of your own class.
  128. */
  129. @interface JSONModel : NSObject <AbstractJSONModelProtocol, NSSecureCoding>
  130. /** @name Creating and initializing models */
  131. /**
  132. * Create a new model instance and initialize it with the JSON from a text parameter. The method assumes UTF8 encoded input text.
  133. * @param string JSON text data
  134. * @param err an initialization error or nil
  135. * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON,
  136. * or a property type in your model is not supported by JSONValueTransformer and its categories
  137. * @see initWithString:usingEncoding:error: for use of custom text encodings
  138. */
  139. -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
  140. /**
  141. * Create a new model instance and initialize it with the JSON from a text parameter using the given encoding.
  142. * @param string JSON text data
  143. * @param encoding the text encoding to use when parsing the string (see NSStringEncoding)
  144. * @param err an initialization error or nil
  145. * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON,
  146. * or a property type in your model is not supported by JSONValueTransformer and its categories
  147. */
  148. -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
  149. -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
  150. -(instancetype)initWithData:(NSData *)data error:(NSError **)error;
  151. /** @name Exporting model contents */
  152. /**
  153. * Export the whole object to a dictionary
  154. * @return dictionary containing the data model
  155. */
  156. -(NSDictionary*)toDictionary;
  157. /**
  158. * Export the whole object to a JSON data text string
  159. * @return JSON text describing the data model
  160. */
  161. -(NSString*)toJSONString;
  162. /**
  163. * Export the whole object to a JSON data text string
  164. * @return JSON text data describing the data model
  165. */
  166. -(NSData*)toJSONData;
  167. /**
  168. * Export the specified properties of the object to a dictionary
  169. * @param propertyNames the properties to export; if nil, all properties exported
  170. * @return dictionary containing the data model
  171. */
  172. -(NSDictionary*)toDictionaryWithKeys:(NSArray*)propertyNames;
  173. /**
  174. * Export the specified properties of the object to a JSON data text string
  175. * @param propertyNames the properties to export; if nil, all properties exported
  176. * @return JSON text describing the data model
  177. */
  178. -(NSString*)toJSONStringWithKeys:(NSArray*)propertyNames;
  179. /**
  180. * Export the specified properties of the object to a JSON data text string
  181. * @param propertyNames the properties to export; if nil, all properties exported
  182. * @return JSON text data describing the data model
  183. */
  184. -(NSData*)toJSONDataWithKeys:(NSArray*)propertyNames;
  185. /** @name Batch methods */
  186. /**
  187. * If you have a list of dictionaries in a JSON feed, you can use this method to create an NSArray
  188. * of model objects. Handy when importing JSON data lists.
  189. * This method will loop over the input list and initialize a data model for every dictionary in the list.
  190. *
  191. * @param array list of dictionaries to be imported as models
  192. * @return list of initialized data model objects
  193. * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON,
  194. * or a property type in your model is not supported by JSONValueTransformer and its categories
  195. * @exception JSONModelInvalidDataException thrown when the input data does not include all required keys
  196. * @see arrayOfDictionariesFromModels:
  197. */
  198. +(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array __attribute__((deprecated("use arrayOfModelsFromDictionaries:error:")));
  199. +(NSMutableArray*)arrayOfModelsFromDictionaries:(NSArray*)array error:(NSError**)err;
  200. +(NSMutableArray*)arrayOfModelsFromData:(NSData*)data error:(NSError**)err;
  201. +(NSMutableArray*)arrayOfModelsFromString:(NSString*)string error:(NSError**)err;
  202. +(NSMutableDictionary*)dictionaryOfModelsFromDictionary:(NSDictionary*)dictionary error:(NSError**)err;
  203. +(NSMutableDictionary*)dictionaryOfModelsFromData:(NSData*)data error:(NSError**)err;
  204. +(NSMutableDictionary*)dictionaryOfModelsFromString:(NSString*)string error:(NSError**)err;
  205. /**
  206. * If you have an NSArray of data model objects, this method takes it in and outputs a list of the
  207. * matching dictionaries. This method does the opposite of arrayOfObjectsFromDictionaries:
  208. * @param array list of JSONModel objects
  209. * @return a list of NSDictionary objects
  210. * @exception JSONModelTypeNotAllowedException thrown when unsupported type is found in the incoming JSON,
  211. * or a property type in your model is not supported by JSONValueTransformer and its categories
  212. * @see arrayOfModelsFromDictionaries:
  213. */
  214. +(NSMutableArray*)arrayOfDictionariesFromModels:(NSArray*)array;
  215. +(NSMutableDictionary*)dictionaryOfDictionariesFromModels:(NSDictionary*)dictionary;
  216. /** @name Comparing models */
  217. /**
  218. * The name of the model's property, which is considered the model's unique identifier.
  219. * You can define Index property by using the Index protocol:
  220. * @property (strong, nonatomic) NSString&lt;Index&gt;* id;
  221. */
  222. -(NSString*)indexPropertyName;
  223. /**
  224. * Overridden NSObject method to compare model objects. Compares the &lt;Index&gt; property of the two models,
  225. * if an index property is defined.
  226. * @param object a JSONModel instance to compare to for equality
  227. */
  228. -(BOOL)isEqual:(id)object;
  229. /**
  230. * Comparison method, which uses the defined &lt;Index&gt; property of the two models, to compare them.
  231. * If there isn't an index property throws an exception. If the Index property does not have a compare: method
  232. * also throws an exception. NSString and NSNumber have compare: methods, and in case the Index property is
  233. * a another custom class, the programmer should create a custom compare: method then.
  234. * @param object a JSONModel instance to compare to
  235. */
  236. -(NSComparisonResult)compare:(id)object;
  237. /** @name Validation */
  238. /**
  239. * Overwrite the validate method in your own models if you need to perform some custom validation over the model data.
  240. * This method gets called at the very end of the JSONModel initializer, thus the model is in the state that you would
  241. * get it back when initialized. Check the values of any property that needs to be validated and if any invalid values
  242. * are encountered return NO and set the error parameter to an NSError object. If the model is valid return YES.
  243. *
  244. * NB: Only setting the error parameter is not enough to fail the validation, you also need to return a NO value.
  245. *
  246. * @param error a pointer to an NSError object, to pass back an error if needed
  247. * @return a BOOL result, showing whether the model data validates or not. You can use the convenience method
  248. * [JSONModelError errorModelIsInvalid] to set the NSError param if the data fails your custom validation
  249. */
  250. -(BOOL)validate:(NSError**)error;
  251. /** @name Key mapping */
  252. /**
  253. * Overwrite in your models if your property names don't match your JSON key names.
  254. * Lookup JSONKeyMapper docs for more details.
  255. */
  256. +(JSONKeyMapper*)keyMapper;
  257. /**
  258. * Sets a key mapper which affects ALL the models in your project. Use this if you need only one mapper to work
  259. * with your API. For example if you are using the [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase] it is more
  260. * likely that you will need to use it with ALL of your models.
  261. * NB: Custom key mappers take precedence over the global key mapper.
  262. * @param globalKeyMapper a key mapper to apply to all models in your project.
  263. *
  264. * Lookup JSONKeyMapper docs for more details.
  265. */
  266. +(void)setGlobalKeyMapper:(JSONKeyMapper*)globalKeyMapper;
  267. /**
  268. * Indicates whether the property with the given name is Optional.
  269. * To have a model with all of its properties being Optional just return YES.
  270. * This method returns by default NO, since the default behaviour is to have all properties required.
  271. * @param propertyName the name of the property
  272. * @return a BOOL result indicating whether the property is optional
  273. */
  274. +(BOOL)propertyIsOptional:(NSString*)propertyName;
  275. /**
  276. * Indicates whether the property with the given name is Ignored.
  277. * To have a model with all of its properties being Ignored just return YES.
  278. * This method returns by default NO, since the default behaviour is to have all properties required.
  279. * @param propertyName the name of the property
  280. * @return a BOOL result indicating whether the property is ignored
  281. */
  282. +(BOOL)propertyIsIgnored:(NSString*)propertyName;
  283. /**
  284. * Indicates the protocol name for an array property.
  285. * Rather than using:
  286. * @property (strong) NSArray<MyType>* things;
  287. * You can implement protocolForArrayProperty: and keep your property
  288. * defined like:
  289. * @property (strong) NSArray* things;
  290. * @param propertyName the name of the property
  291. * @return an NSString result indicating the name of the protocol/class
  292. * that should be contained in this array property. Return nil to indicate
  293. * no contained protocol.
  294. */
  295. +(NSString*)protocolForArrayProperty:(NSString *)propertyName;
  296. /**
  297. * Merges values from the given dictionary into the model instance.
  298. * @param dict dictionary with values
  299. * @param useKeyMapping if YES the method will use the model's key mapper and the global key mapper, if NO
  300. * it'll just try to match the dictionary keys to the model's properties
  301. */
  302. - (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping __attribute__((deprecated("use mergeFromDictionary:useKeyMapping:error:")));
  303. - (void)mergeFromDictionary:(NSDictionary *)dict useKeyMapping:(BOOL)useKeyMapping error:(NSError **)error;
  304. @end