Code Monkey home page Code Monkey logo

uacaps / nsobject-objectmap Goto Github PK

View Code? Open in Web Editor NEW
434.0 38.0 52.0 1.45 MB

This is a drop-in category of NSObject that makes it easy to initialize custom objects from JSON or XML (SOAP included), and to serialize those objects back into JSON/XML. It only requires a little bit of set-up - and then you never have to fuss with creating your own serialization and initialization methods for each custom NSObject ever again.

License: BSD 3-Clause "New" or "Revised" License

Objective-C 99.53% Ruby 0.36% Shell 0.11%

nsobject-objectmap's Introduction

NSObject+ObjectMap

This is a drop-in category of NSObject that makes it easy to initialize custom objects from JSON or XML (SOAP included), and to serialize those objects back into JSON/XML. It only requires a little bit of set-up - and then you never have to fuss with creating your own serialization and initialization methods for each custom NSObject ever again.

Version: 2.0

ScreenShot


Set-Up

The only step is to add NSObject+ObjectMap.{h,m} into your project. That's really it.


Working with JSON

Creating your Objects

This step requires knowing what the JSON coming back will look like. Examine your data source and create your custom NSObject classes to match this. For instance, say you have JSON coming back like this:

{
  Username : "Big Al",
  Password : "r0llt1d3",
	Color : "Crimson",
	Location : "Tuscaloosa, AL",
	Championships: 15
}

If this were the case, you would create your custom NSObject where its properties match this:

@property (nonatomic, retain) NSString *Username;
@property (nonatomic, retain) NSString *Password;
@property (nonatomic, retain) NSString *Color;
@property (nonatomic, retain) NSString *Location;
@property (nonatomic, retain) NSNumber *Championships;

The beautiful thing about this class is that, beyond basic Objective-C classes like NSString and NSNumber, it can handle any object that you create as well. Let's call the previous class definition User - and now let's make an object that has a User class object for a property.

// JSON snippet
{
	Name : "Bryant-Denny",
	CreatedByUser : {
		Username : "Big Al",
		Password : "r0llt1d3",
		Color : "Crimson",
		Location : "Tuscaloosa, AL",
		Championships: 15
	}
}

// Place.h
@property (nonatomic, retain) NSString *Name;
@property (nonatomic, retain) User *CreatedByUser;

NSObject+ObjectMap works by deserializing the JSON and matching the various keys in the JSON packet with the various properties of your objects you want to map to. The caveat of this design is that you must name your properties the exact same as the keys coming back or that property will be left uninitialized (nil) when the deserialization is done.

Working with Arrays

Unfortunately with JSON you're flying blind with regards to the type of objects encased in arrays, one more set up step is necessary for JSON deserialization to an NSObject. If your custom NSObject contains one or more NSArray(s), you need to create a custom init method for this object (or include the following code in your already created custom init). This method is creating an NSDictionary called propertyArrayMap with key/value pairs that match the property name (key) with the type of object you want the NSArray to contain (value). When the object is created from your JSON packet, and the property it is working on is an NSArray, it will check this dictionary to find what kind of objects it needs to create inside the array. Here's an example of setting it up:

// JSON snippet
{
	Name : "Billy",
	FavoriteColors : ["Red","Blue","Tangerine"],
	FavoritePeople : [{
		Name : "Jenny",
		FavoriteColors: [@"Orange","Black"],
		FavoritePeople: []
	},{
		Name : "Ben",
		FavoriteColors: ["Silver","Emerald","Aquamarine"],
		FavoritePeople: []
	}]
}


// Person.h
@property (nonatomic, retain) NSString *Name;
@property (nonatomic, retain) NSArray *FavoriteColors;
@property (nonatomic, retain) NSArray *FavoritePeople;


// Person.m
-(id)init {
	self = [super init];
	if (self) {
		[self setValue:@"NSString" forKeyPath:@"propertyArrayMap.FavoriteColors"];
		[self setValue:@"Person" forKeyPath:@"propertyArrayMap.FavoritePeople"];
	}
	return self;
}

So in this example, we have a JSON string that represents a Person. This Person has a name and two array properties, FavoriteColors and FavoritePeople. FavoriteColors is an array of strings and FavoritePeople is an array of Person objects. As you can tell, the custom init method we created for Person.m sets the propertyArrayMap up to handle what type of object should be contained (in the setValue) and what key to match it to (forKeyPath). As with the other properties and keys mentioned earlier, make sure that these are spelled correctly for proper deserialization and object creation.

Going from JSON to Object

At this point, you should have your custom NSObjects created and your JSON data returning from a webservice, ready to be turned directly into those objects. Now for the easy part. Use the built in NSJSONSerialization methods to turn your JSON data into an NSDictionary or an NSArray, then we're going to pass that into a method that will return your custom NSObject from that. We're going to use the Person JSON snippet from earlier to illustrate this:

// JSON snippet
{
	Name : "Billy",
	FavoriteColors : ["Red","Blue","Tangerine"],
	FavoritePeople : [{
		Name : "Jenny",
		FavoriteColors: [@"Orange","Black"],
		FavoritePeople: []
	},{
		Name : "Ben",
		FavoriteColors: ["Silver","Emerald","Aquamarine"],
		FavoritePeople: []
	}]
}

// Turn that JSON into an NSDictionary, then into your Person object
// - jsonData is the NSData equivalent of the JSON snippet above.
NSData *jsonData;

// Now to create the Person object
Person *newPerson = [[Person alloc] initWithJSONData:jsonData];

Using an array almost the exact same, but instead of an NSDictionary returning from the JSON deserialization, you've received an NSArray. If this NSArray contains a bunch of Person objects, use the following method to create this array:

NSArray *peopleArray = [NSObject arrayOfType:[Person class] fromJSONData:jsonData];

Serializing Object to JSON

Most modern web services and APIs use JSON post data to pass objects that can be handled server-side. Using this class to create your JSON data could not be easier.

Person *newPerson = [[Person alloc] init];
NSData *jsonData = [newPerson JSONData];

To see a string representation of what that JSON packet would look like, use the [NSObject JSONString] method that returns an NSString instead of NSData. You can make sure this is valid JSON by using any number of validation tools online like jsonlint.com.

Troubleshooting

Because of the caveats listed earlier, here's a list of items to check and consider if the class isn't working like it should:

  • Properties of objects are named the exact same as the corresponding keys in your JSON
  • Your JSON object is actually an NSDictionary (or NSArray), and didn't fail in deserialization
  • If your NSObjects contain NSArray properties, you used the custom init method to set up the propertyArrayMap keys/values and named those correctly
  • Your OMDateFormat/OMTimeZone defines were set to the correct format

Working with XML (and SOAP)

Creating your Objects

This step requires knowing what the XML coming back will look like. Examine your data source and create your custom NSObject classes to match this. For instance, say you have XML coming back like this:

<MyObject>
	<Username>Big Al</Username>
	<Password>r0llt1d3</Password>
	<Color>Crimson</Color>
	<Location>Tuscaloosa, AL</Location>
	<Championships>15</Championships>
<MyObject>

If this were the case, you would create your custom NSObject named MyObject where its properties match this:

@property (nonatomic, retain) NSString *Username;
@property (nonatomic, retain) NSString *Password;
@property (nonatomic, retain) NSString *Color;
@property (nonatomic, retain) NSString *Location;
@property (nonatomic, retain) NSNumber *Championships;

Serialization/Deserialization

Just like the JSON side of things, nested complex objects are supported in XML. Also, there is no need to specify array types, so working with XML is arguably more simple. To serialize an object to XML, simply do the following:

MyObject *object = [[MyObject alloc] init];

//*** Fill in object properties with data here ***

NSData *xmlData = [object XMLData];

//*** Send data over web ***

Deserializing back from XML is just as easy:

// XML String of Object
NSString *xmlString = @"<MyObject>
	<Username>Big Al</Username>
	<Password>r0llt1d3</Password>
	<Color>Crimson</Color>
	<Location>Tuscaloosa, AL</Location>
	<Championships>15</Championships>
<MyObject>";

// XML Data
NSData *xmlData = [xmlString dataUsingEncoding:NSUTF8StringEncoding];

// Create MyObject
MyObject *customObject = [[MyObject alloc] initWithXMLData:xmlData];

Note on SOAP: At this point, only simple, tag-driven SOAP is supported. Support for more complex namespace and attribute handling will come if the need arises. Feel free to make a pull request if you find a great way to handle more complex SOAP.


Working with NSDates

In the NSObject+ObjectMap.h file there are two #define constants representing the format/timezone information for NSDate deserialization. Match these to the JSON/XML you are getting back so that NSDateFormatter creates the NSDate objects correctly. These properties are:

  • OMDateFormat
  • OMTimeZone

Demos

To see NSObject+ObjectMap in action, check out one of our many sample Xcode projects under the Demos folder in the top-level. The Google Places demo illustrates NSObject+ObjectMap's JSON handling while the Weather demo takes care of XML. Make sure to check out their READMEs to figure out any setup work before running.

screenshot screenshot


Unit Tests

We have an entire new Unit Testing suite to make sure ObjectMap is actually working after any changes to it. You can run this by opening the UnitTests.xcodeproj under the Tests folder. Just hit Cmd - U on the keyboard to run them and watch to see if it says "Tests Succeeded" on screen. Sometimes it will say "Tests Failed", but if you look in each of the TestCase classes, you will see green or red diamonds by each method. A green diamond means it passed, and a red diamond means it failed.


Cocoapods

Cocoapods is a dependency manager for Objective-C code, and is wonderful for setting up your projects from the start and maintaing them through different versions. When NSObject+ObjectMap.{h,m} updates, you can always get the newest version by changing your podspec file to include the following line to make sure your project stays up to date:

pod 'NSObject-ObjectMap', '~> 2.0'


License

Copyright (c) 2012 The Board of Trustees of The University of Alabama All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the University nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

nsobject-objectmap's People

Contributors

adfleshner avatar bennyguitar avatar ibru avatar kevinhong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nsobject-objectmap's Issues

JSON-Boolean <---> Objective-C BOOL

I use a JSON API with boolean properties.
For example:
{
     "is_used": false,
     "is_unique": true
}

This allows your class to deal only during deserialization, but not in the serialization. What would be a good way to solve this?

Self closing tag with spaces.

I have a node called <StatusMessage /> but it seems to cause issues with the rest of the properties not being filled out.

I think it's the space between StatusMessage and the />

Help creating my custom object class

Hello, I am looking a way to deserialize a xml response. This is the xml that I receipt:
 
xml

And what I need is the token property in this case. I don´t understand how is the structure of the class I have to create, always get a null log... Any help please?

Handling the attributes in xml

Now there is no manipulation for attributes they are simply ignored. Adding this will completely avoid the effort of xml parsing.
It should be better to map the attributes inside a tag to the same named properties within the object as in case of elements do
for eg : Need to handle the tag
screen shot 2014-01-07 at 10 21 24 am

How to fetch Soap String

I have used [[Class alloc]initWithSOAPData:operation.responseData]; . When I tried to fetch Soap String app crashed with this error '-[Class Header]: unrecognized selector sent to instance .

Subclassing - Init infinite loop

How does subclassing work using the initWithJSONData constructor?
Given the following class constructor (invoked only once):


- (id)init {
    NSData* raw = [NSData dataWithContentsOfFile:
                   [[NSBundle mainBundle] pathForResource:@"user" ofType:@"json"]];
    if (self = [MCUser initWithJSONData:raw]) {

    }
    return self;
}

Replacing MCUser initWith.. with self or super also brings the same result.

The app enters an infinite loop (EXC_BAD_ACCESS).
Any idea how use it properly?

Complex JSon not able to convert to class object

Hi,
I have complex Json data. And when I am trying to convert to custom class it is returning nil.
Below is the example of JSon data.
{
"FiltersCount":{
"isFemale":[
{"Id":"T","Count":1310},
{"Id":"F","Count":146},
{"Id":"all_count","Count":1456}]
}
}

I am using Swift and trying to convert to below class's object.
========START=========
import Foundation
class ResultFilterCount : NSObject{
var FiltersCount : FiltersKeyValue?
override init(){
super.init()
if(self != nil){
self.setValue("FiltersKeyValue", forKeyPath: "propertyArrayMap.FiltersCount")
}
}
}

class FiltersKeyValue: NSObject{
var isFemale: [FilterResultModel]?
override init(){
super.init()
self.setValue("FilterResultModel", forKeyPath: "propertyArrayMap.isFemale")
}
}

class FilterResultModel: NSObject{
var Id : NSString?
var Count: NSNumber?
override init(){
super.init()
self.setValue("NSString", forKeyPath: "propertyArrayMap.Id")
self.setValue("NSNumber", forKeyPath: "propertyArrayMap.Count")
}
}

========END=========

This is the code to convert to class.
var data = results as NSData
var pList = ResultFilterCount(JSONData: data)

Result: It returns nil.
Expected Result : It should return ResultFilterCount object with different properties and values.

thanks
ankur

Doubt

Hi friend

How to pass the url and parameter. New developer for SOAP can u explain me.

Wsdl : http://82.178.20.92:8080/CRM2/services/SearchBusiness.ws?wsdl
function : getSearchBusiness
Parameters : array( 'businessCode' => '{"businessCode":[]}',
'businessName' => '@""',
'location' => @"",
'categoryKeyword' => @"",
PageNumber' => 1,
'paginationSize' => 10,
'languageCode' => en''
)

Double Alloc

In our main methods (initWithData:type: and their JSON/XML/SOAP equivalents), we're basically double alloc-ing, and I'm not sure if this has any negative effects. Does ARC take care of this? I know it can't be great practice, and we're working on refactoring things to make JSON/XML/SOAP -> Objects make the most sense from a semantic/conceptual standpoint.

Does anyone have thoughts on this?

Suddenly stopped working for a simple nested object

Hi,

I am using this library for the last 4 years. Now suddenly it's not working for a simple object. I'm using an Apple M1 mac could this be an issue? I have response like this

"dateTime": "26-Apr-2022 9:00:06 AM",
            "postType": "FRIEND",
            "postCreationTimestamp": 1650963267,
            "linkPreview": null,
            "videoStreaming": {
                "channelName": "CDC6423B-331C-4E21-AA21-A45C8D36CFA8"
            },

And this is my Data Model properties. I have not added all properties here.

@Property (nonatomic,strong) NSNumber* postCreationTimestamp;
//Added For Live Video Streaming
@Property (nonatomic,strong) DNLiveStreamingChannel* videoStreaming;

The issue I'm getting is videoStreaming is always getting nil even if it's available in response. However other objects are getting parsed properly. How do I fix this issue?

UPDATE - When I debugged it I found that it's type is always NSNULL instead of class Type

null node get value error

hi: i found a bug
such as this xml:PEK


AM

at last the CONTINENTCN node value is[AM] how can solve?

2013-11-14 11 04 08

Specific mapping from JSON to NSObject property

Automatic mapping is cool, but sometimes you just don't want to use the same names from the JSON for your object properties.
Could you be interested in this feature? If yes, I will take care of implementing it in my spare time. I was thinking of something like creating a protocol and asking the object for a mapping between JSON fields and its properties. If you don't implement the protocol, everything will work as now, otherwise you can customize things a little bit more.
I was also thinking on creating another protocol for parsing and trasforming (or normalize) the values from JSON (or XML) before setting the properties, but this could be an issue on its own.
Let me know what you think!
Thanks

Crash because of NULL properties and nil objects

Til know, I've faced two issues
First, if a key exists in the JSON dictionary and not exist in the NSObject it'll cause an expcetion.
The fix:
in NSObject+ObjectMap.m from line 391 to line 402 replace them with (put them inside if condition that checks if the property is not null)

if(property != NULL){
                                NSString *classType = [self typeFromProperty:property];
                                // check if NSDate or not
                                if ([classType isEqualToString:@"T@\"NSDate\""]) {
                                    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
                                    [formatter setDateFormat:OMDateFormat];
                                    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:OMTimeZone]];
                                    NSString *dateString = [[nestedArray[xx] objectForKey:newKey] stringByReplacingOccurrencesOfString:@"T" withString:@" "];
                                    [nestedObj setValue:[formatter dateFromString:dateString] forKey:newKey];
                                }
                                else {
                                    [nestedObj setValue:[nestedArray[xx] objectForKey:newKey] forKey:newKey];
                                }
                            }

The second issue is in the same file line 411 before adding the object to the array, make sure it's not a nil object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.