├── .gitignore ├── .travis.yml ├── LICENSE ├── NSJSONSerialization-NSNullRemoval.podspec ├── NSNullRemovalTest ├── NSNullRemovalTest.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── NSNullRemovalTest.xcscheme ├── NSNullRemovalTest │ ├── NSNullRemovalTest-Prefix.pch │ └── main.m └── Tests │ └── Tests-Prefix.pch ├── README.md ├── Source ├── NSJSONSerialization+RemovingNulls.h └── NSJSONSerialization+RemovingNulls.m └── Tests └── JRTNullStrippingTest.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_project: NSNullRemovalTest/NSNullRemovalTest.xcodeproj 3 | xcode_scheme: NSNullRemovalTest 4 | xcode_sdk: macosx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Richard Turton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /NSJSONSerialization-NSNullRemoval.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'NSJSONSerialization-NSNullRemoval' 3 | s.version = '1.0.1' 4 | s.license = 'MIT' 5 | 6 | s.summary = 'Categories on NSJSONSerialization, NSMutableDictionary and NSMutableArray to recursively remove NSNull objects returned from JSON services.' 7 | s.homepage = 'https://github.com/jrturton/NSJSONSerialization-NSNullRemoval' 8 | s.author = { 'Richard Turton' => 'jrturton@gmail.com' } 9 | s.source = { :git => 'https://github.com/jrturton/NSJSONSerialization-NSNullRemoval.git', :tag => s.version.to_s } 10 | 11 | s.source_files = 'Source/*.{h,m}' 12 | 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /NSNullRemovalTest/NSNullRemovalTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 27D3E47019470CF6001A3637 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D3E46F19470CF6001A3637 /* Foundation.framework */; }; 11 | 27D3E47319470CF6001A3637 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D3E47219470CF6001A3637 /* main.m */; }; 12 | 27D3E48319470D7E001A3637 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D3E48219470D7E001A3637 /* XCTest.framework */; }; 13 | 27D3E49319470DB5001A3637 /* JRTNullStrippingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D3E49219470DB5001A3637 /* JRTNullStrippingTest.m */; }; 14 | 27D3E49719470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D3E49619470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m */; }; 15 | 27D3E49819470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D3E49619470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 27D3E48D19470D7E001A3637 /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 27D3E46419470CF6001A3637 /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = 27D3E46B19470CF6001A3637; 24 | remoteInfo = NSNullRemovalTest; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 27D3E46C19470CF6001A3637 /* NSNullRemovalTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NSNullRemovalTest; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 27D3E46F19470CF6001A3637 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | 27D3E47219470CF6001A3637 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 27D3E47519470CF6001A3637 /* NSNullRemovalTest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSNullRemovalTest-Prefix.pch"; sourceTree = ""; }; 33 | 27D3E48119470D7E001A3637 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 27D3E48219470D7E001A3637 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 35 | 27D3E48C19470D7E001A3637 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 36 | 27D3E49219470DB5001A3637 /* JRTNullStrippingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JRTNullStrippingTest.m; path = ../../Tests/JRTNullStrippingTest.m; sourceTree = ""; }; 37 | 27D3E49519470DE0001A3637 /* NSJSONSerialization+RemovingNulls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSJSONSerialization+RemovingNulls.h"; sourceTree = ""; }; 38 | 27D3E49619470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSJSONSerialization+RemovingNulls.m"; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 27D3E46919470CF6001A3637 /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 27D3E47019470CF6001A3637 /* Foundation.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 27D3E47E19470D7E001A3637 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 27D3E48319470D7E001A3637 /* XCTest.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 27D3E46319470CF6001A3637 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 27D3E49419470DE0001A3637 /* Source */, 65 | 27D3E47119470CF6001A3637 /* NSNullRemovalTest */, 66 | 27D3E48419470D7E001A3637 /* Tests */, 67 | 27D3E46E19470CF6001A3637 /* Frameworks */, 68 | 27D3E46D19470CF6001A3637 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 27D3E46D19470CF6001A3637 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 27D3E46C19470CF6001A3637 /* NSNullRemovalTest */, 76 | 27D3E48119470D7E001A3637 /* Tests.xctest */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 27D3E46E19470CF6001A3637 /* Frameworks */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 27D3E46F19470CF6001A3637 /* Foundation.framework */, 85 | 27D3E48219470D7E001A3637 /* XCTest.framework */, 86 | ); 87 | name = Frameworks; 88 | sourceTree = ""; 89 | }; 90 | 27D3E47119470CF6001A3637 /* NSNullRemovalTest */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 27D3E47219470CF6001A3637 /* main.m */, 94 | 27D3E47419470CF6001A3637 /* Supporting Files */, 95 | ); 96 | path = NSNullRemovalTest; 97 | sourceTree = ""; 98 | }; 99 | 27D3E47419470CF6001A3637 /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 27D3E47519470CF6001A3637 /* NSNullRemovalTest-Prefix.pch */, 103 | ); 104 | name = "Supporting Files"; 105 | sourceTree = ""; 106 | }; 107 | 27D3E48419470D7E001A3637 /* Tests */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 27D3E49219470DB5001A3637 /* JRTNullStrippingTest.m */, 111 | 27D3E48519470D7E001A3637 /* Supporting Files */, 112 | ); 113 | path = Tests; 114 | sourceTree = ""; 115 | }; 116 | 27D3E48519470D7E001A3637 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 27D3E48C19470D7E001A3637 /* Tests-Prefix.pch */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 27D3E49419470DE0001A3637 /* Source */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 27D3E49519470DE0001A3637 /* NSJSONSerialization+RemovingNulls.h */, 128 | 27D3E49619470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m */, 129 | ); 130 | name = Source; 131 | path = ../Source; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 27D3E46B19470CF6001A3637 /* NSNullRemovalTest */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 27D3E47A19470CF6001A3637 /* Build configuration list for PBXNativeTarget "NSNullRemovalTest" */; 140 | buildPhases = ( 141 | 27D3E46819470CF6001A3637 /* Sources */, 142 | 27D3E46919470CF6001A3637 /* Frameworks */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = NSNullRemovalTest; 149 | productName = NSNullRemovalTest; 150 | productReference = 27D3E46C19470CF6001A3637 /* NSNullRemovalTest */; 151 | productType = "com.apple.product-type.tool"; 152 | }; 153 | 27D3E48019470D7E001A3637 /* Tests */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 27D3E48F19470D7E001A3637 /* Build configuration list for PBXNativeTarget "Tests" */; 156 | buildPhases = ( 157 | 27D3E47D19470D7E001A3637 /* Sources */, 158 | 27D3E47E19470D7E001A3637 /* Frameworks */, 159 | 27D3E47F19470D7E001A3637 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | 27D3E48E19470D7E001A3637 /* PBXTargetDependency */, 165 | ); 166 | name = Tests; 167 | productName = Tests; 168 | productReference = 27D3E48119470D7E001A3637 /* Tests.xctest */; 169 | productType = "com.apple.product-type.bundle.unit-test"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 27D3E46419470CF6001A3637 /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastUpgradeCheck = 0510; 178 | ORGANIZATIONNAME = "Vertical Turtle"; 179 | TargetAttributes = { 180 | 27D3E48019470D7E001A3637 = { 181 | TestTargetID = 27D3E46B19470CF6001A3637; 182 | }; 183 | }; 184 | }; 185 | buildConfigurationList = 27D3E46719470CF6001A3637 /* Build configuration list for PBXProject "NSNullRemovalTest" */; 186 | compatibilityVersion = "Xcode 3.2"; 187 | developmentRegion = English; 188 | hasScannedForEncodings = 0; 189 | knownRegions = ( 190 | en, 191 | ); 192 | mainGroup = 27D3E46319470CF6001A3637; 193 | productRefGroup = 27D3E46D19470CF6001A3637 /* Products */; 194 | projectDirPath = ""; 195 | projectRoot = ""; 196 | targets = ( 197 | 27D3E46B19470CF6001A3637 /* NSNullRemovalTest */, 198 | 27D3E48019470D7E001A3637 /* Tests */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | 27D3E47F19470D7E001A3637 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 27D3E46819470CF6001A3637 /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 27D3E47319470CF6001A3637 /* main.m in Sources */, 219 | 27D3E49719470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m in Sources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | 27D3E47D19470D7E001A3637 /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 27D3E49319470DB5001A3637 /* JRTNullStrippingTest.m in Sources */, 228 | 27D3E49819470DE0001A3637 /* NSJSONSerialization+RemovingNulls.m in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXTargetDependency section */ 235 | 27D3E48E19470D7E001A3637 /* PBXTargetDependency */ = { 236 | isa = PBXTargetDependency; 237 | target = 27D3E46B19470CF6001A3637 /* NSNullRemovalTest */; 238 | targetProxy = 27D3E48D19470D7E001A3637 /* PBXContainerItemProxy */; 239 | }; 240 | /* End PBXTargetDependency section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | 27D3E47819470CF6001A3637 /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 259 | COPY_PHASE_STRIP = NO; 260 | GCC_C_LANGUAGE_STANDARD = gnu99; 261 | GCC_DYNAMIC_NO_PIC = NO; 262 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 263 | GCC_OPTIMIZATION_LEVEL = 0; 264 | GCC_PREPROCESSOR_DEFINITIONS = ( 265 | "DEBUG=1", 266 | "$(inherited)", 267 | ); 268 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | MACOSX_DEPLOYMENT_TARGET = 10.9; 276 | ONLY_ACTIVE_ARCH = YES; 277 | SDKROOT = macosx; 278 | }; 279 | name = Debug; 280 | }; 281 | 27D3E47919470CF6001A3637 /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ALWAYS_SEARCH_USER_PATHS = NO; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_WARN_BOOL_CONVERSION = YES; 290 | CLANG_WARN_CONSTANT_CONVERSION = YES; 291 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | COPY_PHASE_STRIP = YES; 298 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 299 | ENABLE_NS_ASSERTIONS = NO; 300 | GCC_C_LANGUAGE_STANDARD = gnu99; 301 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | MACOSX_DEPLOYMENT_TARGET = 10.9; 309 | SDKROOT = macosx; 310 | }; 311 | name = Release; 312 | }; 313 | 27D3E47B19470CF6001A3637 /* Debug */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 317 | GCC_PREFIX_HEADER = "NSNullRemovalTest/NSNullRemovalTest-Prefix.pch"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | }; 320 | name = Debug; 321 | }; 322 | 27D3E47C19470CF6001A3637 /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 326 | GCC_PREFIX_HEADER = "NSNullRemovalTest/NSNullRemovalTest-Prefix.pch"; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | }; 329 | name = Release; 330 | }; 331 | 27D3E49019470D7E001A3637 /* Debug */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | FRAMEWORK_SEARCH_PATHS = ( 335 | "$(DEVELOPER_FRAMEWORKS_DIR)", 336 | "$(inherited)", 337 | ); 338 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 339 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 340 | GCC_PREPROCESSOR_DEFINITIONS = ( 341 | "DEBUG=1", 342 | "$(inherited)", 343 | ); 344 | PRODUCT_NAME = "$(TARGET_NAME)"; 345 | WRAPPER_EXTENSION = xctest; 346 | }; 347 | name = Debug; 348 | }; 349 | 27D3E49119470D7E001A3637 /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | FRAMEWORK_SEARCH_PATHS = ( 353 | "$(DEVELOPER_FRAMEWORKS_DIR)", 354 | "$(inherited)", 355 | ); 356 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 357 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | WRAPPER_EXTENSION = xctest; 360 | }; 361 | name = Release; 362 | }; 363 | /* End XCBuildConfiguration section */ 364 | 365 | /* Begin XCConfigurationList section */ 366 | 27D3E46719470CF6001A3637 /* Build configuration list for PBXProject "NSNullRemovalTest" */ = { 367 | isa = XCConfigurationList; 368 | buildConfigurations = ( 369 | 27D3E47819470CF6001A3637 /* Debug */, 370 | 27D3E47919470CF6001A3637 /* Release */, 371 | ); 372 | defaultConfigurationIsVisible = 0; 373 | defaultConfigurationName = Release; 374 | }; 375 | 27D3E47A19470CF6001A3637 /* Build configuration list for PBXNativeTarget "NSNullRemovalTest" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 27D3E47B19470CF6001A3637 /* Debug */, 379 | 27D3E47C19470CF6001A3637 /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | }; 383 | 27D3E48F19470D7E001A3637 /* Build configuration list for PBXNativeTarget "Tests" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 27D3E49019470D7E001A3637 /* Debug */, 387 | 27D3E49119470D7E001A3637 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | }; 391 | /* End XCConfigurationList section */ 392 | }; 393 | rootObject = 27D3E46419470CF6001A3637 /* Project object */; 394 | } 395 | -------------------------------------------------------------------------------- /NSNullRemovalTest/NSNullRemovalTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NSNullRemovalTest/NSNullRemovalTest.xcodeproj/xcshareddata/xcschemes/NSNullRemovalTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /NSNullRemovalTest/NSNullRemovalTest/NSNullRemovalTest-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /NSNullRemovalTest/NSNullRemovalTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NSNullRemovalTest 4 | // 5 | // Created by Richard Turton on 10/06/2014. 6 | // Copyright (c) 2014 Vertical Turtle. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | 14 | @autoreleasepool { 15 | 16 | // insert code here... 17 | NSLog(@"Hello, World!"); 18 | 19 | } 20 | return 0; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /NSNullRemovalTest/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jrturton/NSJSONSerialization-NSNullRemoval.svg)](https://travis-ci.org/jrturton/NSJSONSerialization-NSNullRemoval) 2 | 3 | NSJSONSerialization-NSNullRemoval 4 | ================================= 5 | 6 | Categories on NSJSONSerialization, NSMutableDictionary and NSMutableArray to recursively remove NSNull objects often returned from JSON web services. 7 | 8 | To directly remove from a JSON web response: 9 | 10 | ```objc 11 | stripped = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil removingNulls:YES ignoreArrays:NO]; 12 | ``` 13 | 14 | The `ignoreArrays` parameter will leave `NSNull` objects contained directly within arrays in place, for situations when the count of the returned array is important. 15 | 16 | `NSJSONReadingMutableContainers` will be force added to the options if it is not present, since the null removal depends on it. 17 | 18 | Methods are also available to recursively remove NSNull objects from dictionaries and arrays, if preferred: 19 | ```objc 20 | [mutableArray recursivelyRemoveNulls]; 21 | ``` 22 | or 23 | ```objc 24 | [mutableArray recursivelyRemoveNullsIgnoringArrays:YES]; 25 | ``` 26 | -------------------------------------------------------------------------------- /Source/NSJSONSerialization+RemovingNulls.h: -------------------------------------------------------------------------------- 1 | // NSJSONSerialization+RemovingNulls.h 2 | // Created by Richard Turton on 23/12/2013. 3 | 4 | #import 5 | 6 | @interface NSJSONSerialization (RemovingNulls) 7 | 8 | /// As the base class method, but pass YES to remove nulls from containers, optionally ignoring those in arrays. 9 | +(id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError *__autoreleasing *)error removingNulls:(BOOL)removingNulls ignoreArrays:(BOOL)ignoreArrays; 10 | 11 | @end 12 | 13 | @interface NSMutableDictionary (RemovingNulls) 14 | 15 | -(void)recursivelyRemoveNulls; 16 | -(void)recursivelyRemoveNullsIgnoringArrays:(BOOL)ignoringArrays; 17 | 18 | @end 19 | 20 | @interface NSMutableArray (RemovingNulls) 21 | 22 | -(void)recursivelyRemoveNulls; 23 | -(void)recursivelyRemoveNullsIgnoringArrays:(BOOL)ignoringArrays; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Source/NSJSONSerialization+RemovingNulls.m: -------------------------------------------------------------------------------- 1 | // NSJSONSerialization+RemovingNulls.m 2 | // Created by Richard Turton on 23/12/2013. 3 | 4 | #import "NSJSONSerialization+RemovingNulls.h" 5 | 6 | @implementation NSJSONSerialization (RemovingNulls) 7 | 8 | +(id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError *__autoreleasing *)error removingNulls:(BOOL)removingNulls ignoreArrays:(BOOL)ignoreArrays 9 | { 10 | // Mutable containers are required to remove nulls. 11 | if (removingNulls) 12 | { 13 | // Force add NSJSONReadingMutableContainers since the null removal depends on it. 14 | opt = opt | NSJSONReadingMutableContainers; 15 | } 16 | 17 | id JSONObject = [self JSONObjectWithData:data options:opt error:error]; 18 | 19 | if ((error && *error) || !removingNulls) 20 | { 21 | return JSONObject; 22 | } 23 | 24 | if (![JSONObject isKindOfClass:[NSArray class]] && ![JSONObject isKindOfClass:[NSDictionary class]]) { 25 | 26 | return JSONObject; 27 | } 28 | 29 | [JSONObject recursivelyRemoveNullsIgnoringArrays:ignoreArrays]; 30 | return JSONObject; 31 | } 32 | 33 | @end 34 | 35 | @implementation NSMutableDictionary (RemovingNulls) 36 | 37 | -(void)recursivelyRemoveNulls 38 | { 39 | [self recursivelyRemoveNullsIgnoringArrays:NO]; 40 | } 41 | 42 | - (void)recursivelyRemoveNullsIgnoringArrays:(BOOL)ignoringArrays 43 | { 44 | // First, filter out directly stored nulls 45 | NSMutableArray *nullKeys = [NSMutableArray array]; 46 | NSMutableArray *arrayKeys = [NSMutableArray array]; 47 | NSMutableArray *dictionaryKeys = [NSMutableArray array]; 48 | 49 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) 50 | { 51 | if (obj ==[NSNull null]) 52 | { 53 | [nullKeys addObject:key]; 54 | } 55 | else if ([obj isKindOfClass:[NSDictionary class]]) 56 | { 57 | [dictionaryKeys addObject:key]; 58 | } 59 | else if ([obj isKindOfClass:[NSArray class]]) 60 | { 61 | [arrayKeys addObject:key]; 62 | } 63 | }]; 64 | 65 | // Remove all the nulls 66 | [self removeObjectsForKeys:nullKeys]; 67 | 68 | // Recursively remove nulls from arrays 69 | for (id arrayKey in arrayKeys) 70 | { 71 | NSMutableArray *array = self[arrayKey]; 72 | [array recursivelyRemoveNullsIgnoringArrays:ignoringArrays]; 73 | } 74 | 75 | // Cascade down the dictionaries 76 | for (id dictionaryKey in dictionaryKeys) 77 | { 78 | NSMutableDictionary *dictionary = self[dictionaryKey]; 79 | [dictionary recursivelyRemoveNullsIgnoringArrays:ignoringArrays]; 80 | } 81 | } 82 | 83 | @end 84 | 85 | @implementation NSMutableArray (RemovingNulls) 86 | 87 | -(void)recursivelyRemoveNulls 88 | { 89 | [self recursivelyRemoveNullsIgnoringArrays:NO]; 90 | } 91 | 92 | - (void)recursivelyRemoveNullsIgnoringArrays:(BOOL)ignoringArrays 93 | { 94 | // First, filter out directly stored nulls if required 95 | if (!ignoringArrays) 96 | { 97 | [self filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) 98 | { 99 | return evaluatedObject != [NSNull null]; 100 | }]]; 101 | 102 | } 103 | 104 | NSMutableIndexSet *arrayIndexes = [NSMutableIndexSet indexSet]; 105 | NSMutableIndexSet *dictionaryIndexes = [NSMutableIndexSet indexSet]; 106 | 107 | [self enumerateObjectsUsingBlock:^(id obj,NSUInteger idx, BOOL *stop) 108 | { 109 | if ([obj isKindOfClass:[NSDictionary class]]) 110 | { 111 | [dictionaryIndexes addIndex:idx]; 112 | } 113 | else if ([obj isKindOfClass:[NSArray class]]) 114 | { 115 | [arrayIndexes addIndex:idx]; 116 | } 117 | }]; 118 | 119 | 120 | 121 | // Recursively remove nulls from arrays 122 | for (NSMutableArray *containedArray in [self objectsAtIndexes:arrayIndexes]) 123 | { 124 | [containedArray recursivelyRemoveNullsIgnoringArrays:ignoringArrays]; 125 | } 126 | 127 | // Cascade down the dictionaries 128 | for (NSMutableDictionary * containedDictionary in [self objectsAtIndexes:dictionaryIndexes]) 129 | { 130 | [containedDictionary recursivelyRemoveNullsIgnoringArrays:ignoringArrays]; 131 | } 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Tests/JRTNullStrippingTest.m: -------------------------------------------------------------------------------- 1 | // JRTNullStrippingTest.m 2 | // Created by Richard Turton on 23/12/2013. 3 | 4 | #import 5 | #import "NSJSONSerialization+RemovingNulls.h" 6 | 7 | @interface JRTNullStrippingTest : XCTestCase 8 | 9 | @end 10 | 11 | @implementation JRTNullStrippingTest 12 | 13 | -(void)testRemoveNullsAtTopLevelFromDictionary 14 | { 15 | NSMutableDictionary *dictionaryWithNulls = [self dictionaryWithNulls]; 16 | [dictionaryWithNulls recursivelyRemoveNulls]; 17 | XCTAssertNil(dictionaryWithNulls[@"null"]); 18 | XCTAssertTrue([dictionaryWithNulls[@"one"] isEqual:@1]); 19 | } 20 | 21 | - (NSMutableDictionary *)dictionaryWithNulls 22 | { 23 | return [@{@"one" : @1, @"two" : @2, @"null" : [NSNull null]} mutableCopy]; 24 | } 25 | 26 | - (NSData*)dictionaryDataFromStringWithNulls 27 | { 28 | return [@"{\"one\":1,\"two\":2,\"null\":null}" dataUsingEncoding:NSUTF8StringEncoding]; 29 | } 30 | 31 | -(void)testRemoveNullsAtTopLevelFromArray 32 | { 33 | NSMutableArray *arrayWithNulls = [self arrayWithNulls]; 34 | [arrayWithNulls recursivelyRemoveNulls]; 35 | XCTAssertTrue([arrayWithNulls containsObject:@1]); 36 | XCTAssertFalse([arrayWithNulls containsObject:[NSNull null]]); 37 | } 38 | 39 | -(void)testIgnoringRemoveNullsAtTopLevelFromArray 40 | { 41 | NSMutableArray *arrayWithNulls = [self arrayWithNulls]; 42 | [arrayWithNulls recursivelyRemoveNullsIgnoringArrays:YES]; 43 | XCTAssertTrue([arrayWithNulls containsObject:@1]); 44 | XCTAssertTrue([arrayWithNulls containsObject:[NSNull null]]); 45 | } 46 | - (NSMutableArray *)arrayWithNulls 47 | { 48 | return [@[@1,@2,[NSNull null]] mutableCopy]; 49 | } 50 | 51 | - (NSData*)arrayDataFromStringWithNulls 52 | { 53 | return [@"[1, 2, null]" dataUsingEncoding:NSUTF8StringEncoding]; 54 | } 55 | 56 | -(void)testRemoveNullsFromArrayInDictionary 57 | { 58 | NSMutableDictionary *dictionary = [self dictionaryWithNulls]; 59 | NSMutableArray *array = [self arrayWithNulls]; 60 | [dictionary setObject:array forKey:@"array"]; 61 | [dictionary recursivelyRemoveNulls]; 62 | XCTAssertFalse([array containsObject:[NSNull null]]); 63 | } 64 | 65 | -(void)testIgnoringRemoveNullsFromArrayInDictionary 66 | { 67 | NSMutableDictionary *dictionary = [self dictionaryWithNulls]; 68 | NSMutableArray *array = [self arrayWithNulls]; 69 | [dictionary setObject:array forKey:@"array"]; 70 | [dictionary recursivelyRemoveNullsIgnoringArrays:YES]; 71 | XCTAssertTrue([array containsObject:[NSNull null]]); 72 | } 73 | 74 | -(void)testRemoveNullsFromDictionaryInDictionary 75 | { 76 | NSMutableDictionary *dictionary = [self dictionaryWithNulls]; 77 | NSMutableDictionary *nestedDictionary = [self dictionaryWithNulls]; 78 | [dictionary setObject:nestedDictionary forKey:@"dictionary"]; 79 | [dictionary recursivelyRemoveNulls]; 80 | XCTAssertNil([nestedDictionary objectForKey:@"null"]); 81 | XCTAssertTrue([nestedDictionary[@"one"] isEqual:@1]); 82 | } 83 | 84 | -(void)testRemoveNullsFromDictionaryInArrayInDictionary 85 | { 86 | NSMutableDictionary *dictionary = [self dictionaryWithNulls]; 87 | NSMutableArray *array = [self arrayWithNulls]; 88 | NSMutableDictionary *nestedDictionary = [self dictionaryWithNulls]; 89 | [dictionary setObject:array forKey:@"array"]; 90 | [array addObject:nestedDictionary]; 91 | [dictionary recursivelyRemoveNulls]; 92 | XCTAssertNil([nestedDictionary objectForKey:@"null"]); 93 | XCTAssertTrue([nestedDictionary[@"one"] isEqual:@1]); 94 | NSLog(@"dictionary is %@",dictionary); 95 | } 96 | 97 | -(void)testJSONSerializationIsNotBroken 98 | { 99 | NSMutableDictionary *dictionary = [self dictionaryWithNulls]; 100 | NSMutableArray *array = [self arrayWithNulls]; 101 | NSMutableDictionary *nestedDictionary = [self dictionaryWithNulls]; 102 | [dictionary setObject:array forKey:@"array"]; 103 | [array addObject:nestedDictionary]; 104 | 105 | NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:nil]; 106 | 107 | id standard = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; 108 | id stripped = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil removingNulls:NO ignoreArrays:YES]; 109 | 110 | XCTAssertEqualObjects(standard, stripped); 111 | 112 | stripped = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil removingNulls:YES ignoreArrays:NO]; 113 | 114 | XCTAssertFalse([standard isEqual:stripped]); 115 | } 116 | 117 | -(void)testJSONSerializationErrorStillReturned 118 | { 119 | NSString *invalidJSON = @"I am not JSON!"; 120 | NSData *data = [invalidJSON dataUsingEncoding:NSUTF8StringEncoding]; 121 | 122 | NSError *standard = nil; 123 | NSError *category = nil; 124 | [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&standard]; 125 | [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&category removingNulls:YES ignoreArrays:NO]; 126 | XCTAssertNotNil(standard); 127 | XCTAssertNotNil(category); 128 | XCTAssertEqualObjects(standard, category); 129 | NSLog(@"%@",category); 130 | } 131 | 132 | -(void)testRemoveNullsAtJSONSerializationFromDictionary 133 | { 134 | NSData *dictionaryData = [self dictionaryDataFromStringWithNulls]; 135 | 136 | NSError *standard = nil; 137 | NSError *category = nil; 138 | [NSJSONSerialization JSONObjectWithData:dictionaryData options:NSJSONReadingMutableContainers error:&standard]; 139 | NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:dictionaryData options:NSJSONReadingMutableContainers error:&category removingNulls:YES ignoreArrays:NO]; 140 | XCTAssertNil(standard); 141 | XCTAssertNil([dictionary objectForKey:@"null"]); 142 | } 143 | 144 | -(void)testSendingNoOptionsStillWorks 145 | { 146 | NSData *dictionaryData = [self dictionaryDataFromStringWithNulls]; 147 | 148 | NSError *standard = nil; 149 | NSError *category = nil; 150 | [NSJSONSerialization JSONObjectWithData:dictionaryData options:kNilOptions error:&standard]; 151 | NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:dictionaryData options:kNilOptions error:&category removingNulls:YES ignoreArrays:NO]; 152 | XCTAssertNil(standard); 153 | XCTAssertNil([dictionary objectForKey:@"null"]); 154 | } 155 | 156 | -(void)testRemoveNullsAtJSONSerializationFromArray 157 | { 158 | NSData *arrayData = [self arrayDataFromStringWithNulls]; 159 | 160 | NSError *standard = nil; 161 | NSError *category = nil; 162 | [NSJSONSerialization JSONObjectWithData:arrayData options:NSJSONReadingMutableContainers error:&standard]; 163 | NSArray *array = [NSJSONSerialization JSONObjectWithData:arrayData options:NSJSONReadingMutableContainers error:&category removingNulls:YES ignoreArrays:NO]; 164 | XCTAssertNil(standard); 165 | XCTAssertFalse([array containsObject:[NSNull null]]); 166 | } 167 | 168 | -(void)testForceMutableContainersPreservesFragmentParsing { 169 | 170 | NSData *fragmentData = [@"32" dataUsingEncoding:NSUTF8StringEncoding]; 171 | 172 | NSError *standard = nil; 173 | NSError *category = nil; 174 | [NSJSONSerialization JSONObjectWithData:fragmentData options:NSJSONReadingAllowFragments error:&standard]; 175 | NSNumber *number = [NSJSONSerialization JSONObjectWithData:fragmentData options:NSJSONReadingAllowFragments error:&category removingNulls:YES ignoreArrays:NO]; 176 | 177 | XCTAssertNil(standard); 178 | XCTAssertNotNil(number); 179 | } 180 | 181 | @end 182 | --------------------------------------------------------------------------------