Giter VIP home page Giter VIP logo

keyvalueobjectmapping's Introduction

Introduction

KeyValueObjectMapping is a Cocoa framework that provides an easy way to deal with any key/value type, as JSON, XML, plist and even a common NSDictionary. With no additional framework.

It's made to be used together with a parser, such: NSJSONSerialization, JSONKit, NSXMLParser and other resources, and the main goal is to avoid the tedious work when you need to deal with key/value types.

Features

  • Transform any kind of Key/Value type to a desired object. Can be a JSON, XML, plist or a simple NSDictionary.
  • Don't generate accessors to all the attribute, keep your properties readonly and framework will do it's magic to put the value on the object.
  • Based on Convention over Configuration
    1. If your attribute follow the same name of the key on the NSDictionary everything will be done automatically.
    2. If the keys on source are separated by some character, you can configure which character is and the framework will split and camelcase it to find the properly attribute.
  • Awesome customizations via blocks, change the default behavior when creating an instance or parsing a value. Using DCCustomInitialize and DCCustomParser.
  • Map any key to a specific attribute that doesn't follow the convention using DCObjectMapping.
  • To map an one-to-many relation use DCArrayMapping to tell what is the specific type of elements that will be inserted.
  • Aggregate values to an specific attribute using DCPropertyAggregator.
  • Parse NSDate using a specific date pattern(passed through the configuration) or if it's send on JSON in milliseconds since Jan 1, 1970 (UNIX timestamp) will be parsed with no additional configuration.
  • Having a property pointing to a NSURL, framework will try to use [NSURL URLWithString:] method passing the value as a NSString.

Installation

  • Using CocoaPods, the easier way to manage dependencies on Objective-C world.

  • Using iOS-Universal-Framework Since KeyValueObjectMapping uses iOS-Universal-Framework to build and compile the project, you can easily compile and drag the .framework that iOS-Universal-Framework generates into your application, import the header DCKeyValueObjectMapping.h and start using the framework.

    • Required import:
     	#import <DCKeyValueObjectMapping/DCKeyValueObjectMapping.h>

Usage

KeyValueObjectMapping is a simple object, all you need to do is create a new object and start to transform a dictionary to any classes.

Let's assume that you have some JSON like that:

{
	"id_str": "27924446",
	"name": "Diego Chohfi",
	"screen_name": "dchohfi",
	"location": "São Paulo",
	"description": "Instrutor na @Caelum, desenvolvedor de coração, apaixonado por música e cerveja, sempre cerveja.",
	"url": "http://about.me/dchohfi",
	"protected": false,
	"created_at": "Tue Mar 31 18:01:12 +0000 2009"
}

And your User model looks like:

@interface User : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *screenName;
@property(nonatomic, strong) NSString *location;
@property(nonatomic, strong) NSString *description;
@property(nonatomic, strong) NSURL *url;
@property(nonatomic, strong) BOOL protected;
@property(nonatomic, strong) NSNumber *followersCount;
@property(nonatomic, strong) NSDate *createdAt;
@end

Using any JSON parser you need to transform this NSString to a NSDictionary representation:

NSError *error;
NSDictionary *jsonParsed = [NSJSONSerialization JSONObjectWithData:jsonData
	                              options:NSJSONReadingMutableContainers error:&error];

If you don't use KeyValueObjectMapping you need to create an instance of User type, and set all the properties with the same key name on the dictionary. And transform it when needed.

User *user = [[User alloc] init];
[user setIdStr: [jsonParsed objectForKey: @"id_str"]];
[user setName: [jsonParsed objectForKey: @"name"]];
[user setScreenName: [jsonParsed objectForKey: @"screen_name"]];

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.datePattern = @"eee MMM dd HH:mm:ss ZZZZ yyyy";
NSDate *date = [formatter dateFromString:@"Sat Apr 14 00:20:07 +0000 2012"];

[user setCreatedAt: date];

Boring job, don't you think? So, if you use KeyValueObjectMapping you just need to give the dictionary and the class that you want to create, and everthing else will be made automatically.

DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class]];

Tweet *tweet = [parser parseDictionary:jsonParsed];
NSLog(@"%@ - %@", tweet.idStr, tweet.name);

DCParserConfiguration

If your NSDate pattern are different then the default, which is @"eee MMM dd HH:mm:ss ZZZZ yyyy", you can configure to use a different one. So, there is an object to add custom configuration to the framework.

Using DCParserConfiguration you can change the default behavior of some components, like the default pattern to parse a date.

DCParserConfiguration *config = [DCParserConfiguration configuration];
config.datePattern = @"dd/MM/yyyy";

DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class] andConfiguration: config];

Overriding Key Name for Attribute

If your JSON have some specific key that doesn't match the attribute name you can use DCObjectMapping to map this key to the attribute, the attribute type can be a specific Object either.

Your Tweet model:

@interface Tweet : NSObject
@property(nonatomic, readonly) NSString *idStr;
@property(nonatomic, readonly) NSString *tweetText;

@property(nonatomic, readonly) User *userOwner;

@end

And the JSON received follow the struct:

{
    "id_str": 190957570511478800,
    "text": "Tweet text",
    "user": {
        "name": "Diego Chohfi",
        "screen_name": "dchohfi",
        "location": "São Paulo"
    }
}

Using DCObjectMapping you can parse this JSON and override the key names like that:

DCParserConfiguration *config = [DCParserConfiguration configuration];

DCObjectMapping *textToTweetText = [DCObjectMapping mapKeyPath:@"text" toAttribute:@"tweetText" onClass:[Tweet class]];
DCObjectMapping *userToUserOwner = [DCObjectMapping mapKeyPath:@"user" toAttribute:@"userOwner" onClass:[Tweet class]];

[config addObjectMapping:textToTweetText];
[config addObjectMapping:userToUserOwner];

DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class]  andConfiguration:config];
Tweet *tweetParsed = [parser parseDictionary:json];

Parsing NSArray properties

Since Objective-C don't support typed collections like Java and other static language we can't figure out what it the type of elements inside a collection. But KeyValueObjectMapping can be configured to learn what is the type of elements that will be added to the collection on the specific attribute for the class.

So, if the model User have many Tweets:

@interface User : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *screenName;
@property(nonatomic, strong) NSString *location;
@property(nonatomic, strong) NSString *description;
@property(nonatomic, strong) NSURL *url;
@property(nonatomic, strong) BOOL protected;
@property(nonatomic, strong) NSNumber *followersCount;
@property(nonatomic, strong) NSDate *createdAt;

@property(nonatomic, strong) NSArray *tweets;

@end

The Tweet looks like:

@interface Tweet : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *text;
@property(nonatomic, strong) NSDate *createdAt;

@end

And the JSON looks like:

{
    "id_str": "27924446",
    "name": "Diego Chohfi",
    "screen_name": "dchohfi",
    "location": "São Paulo",
    "description": "Instrutor na @Caelum, desenvolvedor de coração, apaixonado por música e cerveja, sempre cerveja.",
    "url": "http://about.me/dchohfi",
    "protected": false,
    "created_at": "Tue Mar 31 18:01:12 +0000 2009",
		"tweets" : [
			{
				"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
				"id_str" : 190957570511478784,
				"text" : "Tweet text"
			},
			{
				"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
				"id_str" : 190957570511478784,
				"text" : "Tweet text"
			}
		]
}

Using DCArrayMapping and adding it to the configuration, you tell to the KeyValueObjectMapping how to parse this specific attribute.

DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements:[Tweet class] forAttribute:@"tweets" onClass:[User class]];
											
DCParserConfiguration *config = [DCParserConfiguration configuration];
[config addArrayMapper:mapper];

DCKeyValueObjectMapping *parser = [[DCKeyValueObjectMapping mapperForClass:[User class]  andConfiguration:configuration];
User *user = [parser parseDictionary:jsonParsed];

Aggregating values to specific type

Sometimes you faces an JSON to parse that you don't have access to modify the struct, and you don't want to make your classes follow that specific struct. Using DCPropertyAggregator you can aggregate more than one key/value to a specific attribute on your domain.

So, if your JSON looks like that:

{
	"tweet" : "Some text",
	"latitude" : -23.588453,
	"longitude" : -46.632103,
	"distance" : 100
}

If you follow this JSON struct your objects won't be so organized, right? So, you can make your objects follow something different:

@interface Tweet : NSObject
@property(nonatomic, readonly) NSString *text;
@property(nonatomic, readonly) Location *location;
@end

@interface Location : NSObject
@property(nonatomic, readonly) NSNumber *distance;
@property(nonatomic, readonly) Point *point;
@end

@interface Point : NSObject
@property(nonatomic, readonly) NSNumber *latitude;
@property(nonatomic, readonly) NSNumber *longitude;
@end

And using DCPropertyAggregator to map this specific behavior:

DCPropertyAggregator *aggregteLatLong = [DCPropertyAggregator aggregateKeys:[NSSet setWithObjects:@"latitude", @"longitude", nil] intoAttribute:@"point"];
DCPropertyAggregator *aggregatePointDist = [DCPropertyAggregator aggregateKeys:[NSSet setWithObjects:@"point", @"distance", nil] intoAttribute:@"location"];

DCParserConfiguration *configuration = [DCParserConfiguration configuration];
[configuration addAggregator:aggregteLatLong];
[configuration addAggregator:aggregatePointDist];

DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass:[Tweet class] andConfiguration:configuration];
Tweet *tweet = [parser parseDictionary: json];

Bitdeli Badge

keyvalueobjectmapping's People

Contributors

akinsella avatar alexocode avatar atyschenko avatar benjipetit avatar bhargavms avatar bitdeli-chef avatar bsideup avatar carnun avatar choreo avatar cristianbica avatar darvin avatar dchohfi avatar dougsuriano avatar ha-nyung avatar matzew avatar mungler avatar n8j1s avatar orkenstein avatar qizh avatar vincilbishop 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

keyvalueobjectmapping's Issues

Nice to have: DCDictionaryMapping

It would be nice to have a class like "DCArrayMapping" for NSDictionary, because I have a JSON like this:

{
    "_id":12345,
    "localizedContent":{
        "en":{
            "name":"english name"
        },
        "es":{
            "name":"spanish name"
        }
    }
}

... with an undetermined number of languages, so I'm trying to map it to an object like this:

@interface MyLocalizedContent : NSObject

@property (nonatomic, strong) NSString *name;

@end


@interface MyObject : NSObject

@property (nonatomic, strong) NSNumber *identifier;
@property (nonatomic, strong) NSDictionary *localizedContent;

@end

... and I don't know how to do it without this "DCDictionaryMapping".

CoreData Support

Hey,

In your podspec is written that you support work with CoreData, but in practice I cannot find it in the documentation, nor the tests.

Could you provide a simple example how's that supposed to work?

I thought it would work out of the box, but this is what happens:

MyNSManagedObjectSubclass *contact = [mapping parseDictionary: myDictionary];

in this line I get CoreData: error: Failed to call designated initializer on NSManagedObject class 'MyNSManagedObjectSubclass'.

I know that this in CoreData should look something like:

MyNSManagedObjectSubclass *contact = [NSEntityDescription
                             insertNewObjectForEntityForName:@"MyNSManagedObjectSubclass"
                             inManagedObjectContext:backgroundContext];

.. but I don't know how to populate all the properties in my NSManagedObject with your library.

Thanks,

Kex

Support Parse configuration merging

Implementation provided may be improved, but it works well for the projects I work on.

1 - Interface

@interface DCParserConfiguration (XBAdditions)

-(void) mergeConfig:(DCParserConfiguration *) parserConfiguration;

@end

2 - Implementation

#import "DCParserConfiguration+XBAdditions.h"
#import "DCArrayMapping.h"


@implementation DCParserConfiguration (XBAdditions)

-(void) mergeConfig:(DCParserConfiguration *) parserConfiguration {

    for (DCObjectMapping * objectMapping in parserConfiguration.objectMappers) {
        DCArrayMapping * arrayMapping = [parserConfiguration arrayMapperForMapper:objectMapping];
        if (arrayMapping) {
            [self addArrayMapper:arrayMapping];
        }
        else{
            [self addObjectMapping:objectMapping];
        }
    }

    for (DCPropertyAggregator * propertyAggregator in parserConfiguration.aggregators) {
        [self addAggregator:propertyAggregator];
    }

    for (DCCustomInitialize * customInitialize in parserConfiguration.customInitializers) {
        [self addCustomInitializersObject:customInitialize];
    }

    for (DCCustomParser * customParser in parserConfiguration.customParsers) {
        [self addCustomParsersObject:customParser];
    }

}

@end

synthesize to value confuses the parser

Here is my little model:

@interface Employee : NSObject

@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSArray* tasks;

@end

@interface Task : NSObject

@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@end

Now, when using the @synthesizelike

@synthesize tasks = _tasks;

the parser no longer works:

-(void) testCustomMap {

    // set up some "faked JSON parsed Dictionary"
    NSMutableDictionary* task1 = [NSMutableDictionary dictionary];
    [task1 setValue:@"Title1" forKey:@"title"];
    [task1 setValue:@"Description1" forKey:@"desc"];

    NSMutableDictionary* task2 = [NSMutableDictionary dictionary];
    [task2 setValue:@"Title2" forKey:@"title"];
    [task2 setValue:@"Description2" forKey:@"desc"];

    NSMutableDictionary* employee1 = [NSMutableDictionary dictionary];
    [employee1 setValue:@"Matthias" forKey:@"name"];
    [employee1 setValue:[NSArray arrayWithObjects:task1, task2, nil] forKey:@"tasks"];


    DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements:[Task class] forAttribute:@"tasks" onClass:[Employee class]];

    DCParserConfiguration *config = [DCParserConfiguration configuration];
    [config addArrayMapper:mapper];

    DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass:[Employee class]  andConfiguration:config];


    Employee *matthias = [parser parseDictionary:employee1];


    STAssertEqualObjects(@"Matthias", matthias.name, @"");

    NSLog(@"\n\n\n==>%@\n\n", [[matthias.tasks objectAtIndex:0] class]);

}

The class here is __NSCFDictionary. When using the @synthesize with out applying a value to it, like

@synthesize tasks;

The class is Task

Exception raised when using dynamic properties

Exception raised :

[<... 0x8a96eb0> setValue:forUndefinedKey:]: the entity ... is not key value coding-compliant for the key ""

Method call :

DCAttributeSetter assingValue:forAttributeName:andAttributeClass:onObject:

Line :

[object setValue:value forKey:attributeName]

The issue appear when i work with subclasses of NSManageObject

In DCDynamicAttribute initWithAttributeDescription:forKey:onClass:attributeName:converter:
findTypeName: return @"" because the description of a dynamic property doen't end with the name of the instance variable associated : @"T@"NSString",&,D,N"

NSDate from unixtime in milliseconds does not work

Hello,

since you are using [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)] in the DateConverter class, you need to pass this interval in SECONDS, not millis.
Do you prefer to stay with seconds (and change the documentation where you say milliseconds) or /1000 the value? I can send a pull request for the second one.

I'm getting value for chatTextModel.message.origin but getting nil for chatTextModel.message.flight_legs. Details are in the description.

My Json object -

{
"type": "flight",
"senderid": "101",
"message": {
"origin": "Bangalore",
"destination": "Paris",
"start_date": "21-04-2017",
"end_date": "27-04-2017",
"price": "300",
"currency": "$",
"fare_rules": "This is the fare rules text.",
"refundable": "true",
"check_in_baggage": "30 kg",
"legs": [
{
"index": "1",
"airline_id": "2003",
"flight_number": "TK 721 T",
"name": "Turkish Airline",
"logo": "http://turkish.com/logo.png",
"origin": "Bangalore",
"destination": "Chennai",
"start_date": "21-04-2017",
"end_date": "21-04-2017",
"start_time": "06.20 pm",
"end_time": "8.15 pm",
"journey_time": "2h 30m",
"is_next": "true",
"layover": "3 hours"
},
{
"index": "2",
"airline_id": "4003",
"flight_number": "JT 021",
"name": "Jet Airways",
"logo": "http://jetairways.com/logo.png",
"origin": "Chennai",
"destination": "Paris",
"start_date": "21-04-2017",
"end_date": "21-04-2017",
"start_time": "11.20 pm",
"end_time": "2.15 am",
"journey_time": "3h 10m",
"is_next": "false",
"layover": ""
}
]
}


My models are -

@interface TRChatTypeFlightsModel : NSObject

@Property (strong, nonatomic) NSString *type;
@Property (assign, nonatomic) NSInteger senderid;
@Property (strong, nonatomic) TRChatFlightMessageModel *message;

@EnD

@interface TRChatFlightMessageModel : NSObject

@Property (strong, nonatomic) NSString *origin;
@Property (strong, nonatomic) NSString *destination;
@Property (strong, nonatomic) NSString *start_date;
@Property (strong, nonatomic) NSString *end_date;
@Property (strong, nonatomic) NSString *fare_rules;
@Property (strong, nonatomic) NSArray *flight_legs;

@interface TRChatFlightLegModel : NSObject

@Property (strong, nonatomic) NSString *index;
@Property (strong, nonatomic) NSString *airline_id;
@Property (strong, nonatomic) NSString *is_next;
@Property (strong, nonatomic) NSString *layover;

@EnD


Mapping -

DCParserConfiguration *config = [DCParserConfiguration configuration];

DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements:[TRChatFlightLegModel class] forAttribute:@"legs" onClass:[TRChatFlightMessageModel class]];

[config addArrayMapper:mapper];

DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass:[TRChatTypeFlightsModel class] andConfiguration:config];

TRChatTypeFlightsModel *chatTextModel = [parser parseDictionary:message.data.message];


Getting values for all chatTextModel.message.* but getting nil value for chatTextModel.message.flight_legs. Am I doing anything wrong in Mapping?

DCParserConfiguration addArrayMapper doesn't respect different classes for similar attribute name

My code looks something like this:

static DCParserConfiguration config;
if ( !config )
    {
        config = [DCParserConfiguration configuration];

        [config addArrayMapper:[DCArrayMapping mapperForClassElements:[UnicomAPIProposalsResponseProposal class]
                                                         forAttribute:@"results"
                                                              onClass:[UnicomAPIProposalsResponse class]]];

        [config addArrayMapper:[DCArrayMapping mapperForClassElements:[UnicomAPIBankProposalsResponseBank class]
                                                         forAttribute:@"results"
                                                              onClass:[UnicomAPIBankProposalsResponse class]]];
    }

    pFunction( resultClass ? [[DCKeyValueObjectMapping mapperForClass:resultClass andConfiguration:config] parseDictionary:responseObject] : responseObject );
...

But results of UnicomAPIBankProposalsResponse becomes UnicomAPIProposalsResponseProposal instead of UnicomAPIBankProposalsResponseBank!

WAIDW?

Typo in code example of Cocoapods documentation

In the readme of this github project, we have the following nice and correct example:

DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements:[Tweet class] forAttribute:@"tweets" onClass:[User class]];

But on the website of Cocoapods https://cocoapods.org/pods/DCKeyValueObjectMapping
The same code sample is wrong:

DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements: :[Tweet class] forAttribute:@"tweets"] onClass:[User class]];

There is a useless : before [Tweet class] and and a useless ] after @"tweets"

Edit:
I just found a new error in both this readme and cocoapods:
The following code sample doesn't compile:

DCParserConfiguration *config = [DCParserConfiguration configuration];
[config addArrayMapper:mapper];

DCKeyValueObjectMapping *parser = [[DCKeyValueObjectMapping mapperForClass:[User class] andConfiguration:configuration];
User *user = [parser parseDictionary:jsonParsed];

andConfiguration:configuration must be replaced by andConfiguration:config

Nice to have: addMapper in DCParserConfiguration

Current DCParserConfiguration supports addArrayMapper: and addObjectMapping:
I think it could be nice to introduce addMapper:(id)mapper that supports both object mapping and array mapping in single API. 😄

Whats the cocoapods dependency key here?

I've tried pod 'DCKeyValueObjectMapping' but when I do #import <KeyValueObjectMapping/DCKeyValueObjectMapping.h> the file cannot be found? How am I supposed to use this ? What am I doing wrong here?

Handling unexpected json data

Sometimes the json data type and the type in the model does not match. For example something you expect to be an array is a string. In these cases the parsing should have a error parameter, so that we can get a sensable error if that happens.

Question - Is it safe to use a `static` mapper instance?

Like so:

+ (DCKeyValueObjectMapping *)mapper {
    static DCKeyValueObjectMapping *mapper;

    if (!mapper) {
        @synchronized([self class]) {
            if (!mapper) {
                DCParserConfiguration *config = [DCParserConfiguration configuration];

                [config addObjectMapping:[DCObjectMapping mapKeyPath:@"id"
                                                         toAttribute:@"repositoryId"
                                                             onClass:[self class]]];

                [config addObjectMapping:[DCObjectMapping mapKeyPath:@"description"
                                                         toAttribute:@"descriptionText"
                                                             onClass:[self class]]];

                mapper = [DCKeyValueObjectMapping mapperForClass:[self class] andConfiguration:config];
            }
        }
    }

    return mapper;
}

+ (Repository *)instanceFromDictionary:(NSDictionary *)aDictionary; {
    Repository *instance = [self.mapper parseDictionary:aDictionary];
    return instance;
}

Will my calls to instanceFromDictionary: be thread-safe?

filling an object but not creating it

Hi,

I had a look at your library, and I thing it can help me a lot. But I would like to use it for filling properties of an existing object.

Something like
MyClass *myObject = [[MyClass alloc] init]; NSDictionary *fixture = ...; [myObject parseDictionary:fixture];

But I did not find how to do it? I saw then custom initializer, but I prefer to create directly my instances.

Regards,
Quentin Arnault

Add support for implicit array serialization

It should be possible to parse an array of objects even if the corresponding value in the dictionary is not an array. A use case is as follows: you parse xml that is designed to be an array but only has a single element. The xml parser has no knowledge of the array intention, and most will just parse this to a dictionary, not an array of one element. This library, however, can actually convert these properly with no additional configuration as it knows the target property is an array type. To clarify further with an example:

This json can be parsed into a user with an array of one tweet:

{
"name": "Diego Chohfi",
"tweets": [
{
"id_str": 190957570511478800,
"text": "Tweet text"
}
]
}

However, this too should be parseable as a user with an array of one tweet (provided you specify an array mapping for property "tweets" of course), but I don't see a way to do it currently:

{
"name": "Diego Chohfi",
"tweets":
{
"id_str": 190957570511478800,
"text": "Tweet text"
}
}

Can I track the mapping progress?

Hello!
Is there any way to track the progress? Because I have a few megabytes json file, so it's going to take time and I want do draw a progress bar.
Thanks.

Add tag 1.5 and update podspec

Could You please add Tag 1.5 and update the podspec file? It would be nice to download actual version using Cocoapods

CoreData Sample Code

Hi dchohfi,
I really don't know how to deal with core data object mapping using DCCustomInitialize.
Could you please write a few sample codes for me? THX : )

Unix Timestamp Conversion Error

Unix Timestamp is in milliseconds while dateWithTimeIntervalSince1970: takes a NSTimeInterval in seconds. Currently the conversion is 1000x off.

How to create a real one-to-many relationship?

Hello,

I'm new to KeyValueObjectMapping so my question might sound easy: how to create the two relationships of a one-to-many relationship?

For instance, let's take the example of the User that has many tweets:

    1. There's a NSArray of tweets in the User model
    1. There's a User property in the Tweet model

I figured out to do 1) very easily but can't find a way to do the 2) link. Is there any way to do that?

Cheers,
Martin

see a lot of warning when using the framework in other projects

Any idea about this?

/Users/wu/Library/Developer/Xcode/DerivedData/KeyValueObjectMapping-biwhysvlufvbppeijvcbwdgyamen/Build/Intermediates/KeyValueObjectMapping.build/Release-iphonesimulator/KeyValueObjectMapping.build/Objects-normal/i386/DCPropertyFinder.o

Any Dome

Automatic KeyValue Object Mapping for Objective-C, parse JSON/plist/Dictionary automatically

Cannot comple

Hi,

I've imported #import <DCKeyValueObjectMapping/DCKeyValueObjectMapping.h> as suggested but DCParserConfiguration, norDCArrayMapping` are visible.

Do you have some header in this pod that includes all other headers? Would you mind adding some header with the following imports (and other if necessary):

#import "DCKeyValueObjectMapping.h"
#import "DCParserConfiguration.h"
#import "DCArrayMapping.h"

NSURL property and url with spaces

When model class has an NSURL property and there is an input string associated, which is url and which has a space in it, then it is not converted to a NSURL object, but been left as NSString and been assigned to an NSURL property.

Crash on parsing empty array property

When parsing an empty array from a property it crashes, it asumes it can check objectAtIndex:0 but does not check if the list is not empty.

DCNSArrayConverter line:38

Scheme for running tests

Is there a scheme that could be shared for running the tests? when you first clone this project, you can't verify that the tests work because you don't have the scheme to run them. :)

os x support?

can I use this with os x application? since podspec doesn't specify os x version requirement I can't install it using pod.

add RFC3339 date support

        // Process date
        NSString *RFC3339String = [[NSString stringWithString:dateString] uppercaseString];
        RFC3339String = [RFC3339String stringByReplacingOccurrencesOfString:@"Z" withString:@"-0000"];
        // Remove colon in timezone as it breaks NSDateFormatter in iOS 4+.
        // - see https://devforums.apple.com/thread/45837
        if (RFC3339String.length > 20) {
            RFC3339String = [RFC3339String stringByReplacingOccurrencesOfString:@":" 
                                                                     withString:@"" 
                                                                        options:0
                                                                          range:NSMakeRange(20, RFC3339String.length-20)];
        }
        if (!date) { // 1996-12-19T16:39:57-0800
            [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"]; 
            date = [dateFormatter dateFromString:RFC3339String];
        }
        if (!date) { // 1937-01-01T12:00:27.87+0020
            [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZZZ"]; 
            date = [dateFormatter dateFromString:RFC3339String];
        }
        if (!date) { // 1937-01-01T12:00:27
            [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss"]; 
            date = [dateFormatter dateFromString:RFC3339String];
        }
        if (!date) NSLog(@"Could not parse RFC3339 date: \"%@\" Possible invalid format.", dateString);

NSArray with split string key name

In the json:
{ "holidayhome": {"home_facilities": [ { "facility_description": "Delivery To Park", } ] } }

In the class:
NSArray *homeFacilities;

Because of the underscore split string, the conversion does not work. It works for everything else, only for arrays of objects it does not work. DCArrayMapping

document update

The dateFormat is changed to datePattern. Please update the document.

Enable BitCode

Please enable bit code for the project to effectively support App thinning in iOS9 and WatchOS

Potential memory leak in DCObjectMapping

Since @property without memory attributed use strong for memory storage, converter property in the class DCObjectMapping should be marked with weak attribute to avoid potential memory leak.

This

@interface DCObjectMapping : NSObject <DCMapping>
..
@property(nonatomic, readonly) id <DCValueConverter> converter;
..
@end

Should be changed in this

@interface DCObjectMapping : NSObject <DCMapping>
..
@property(nonatomic, readonly, weak) id <DCValueConverter> converter;
..
@end

Crash on iOS 8 beta 5 if JSON returns a field named "description"

As part of Apple's big refactor job in iOS 8 one of the things they've changed is to turn the -description method in the NSObject protocol into a read only property.

If we have some JSON that returns a field named "description" the library will throw an exception (full stack trace below). This is because it's trying to set the readonly description property on NSObject.

Now I don't think this is really an issue with the KeyValueObjectMapping code itself. Its just that now "description" should be treated as a restricted keyword like "id" and it should be handled by developers with a custom DCObjectMapping map key path.

That being said, it's probably important to make people aware of this. "description" is a far more likely JSON field name than "id" or "alloc" or any other reserved keyword. Maybe you could throw your own exception which could contain a more helpful description than Apple's.

Full Stack Trace:

2014-08-05 16:29:59.327 My_App[64137:321932] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyDataModel 0x7f1f7920> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.'
*** First throw call stack:
(
0 CoreFoundation 0x049a5df6 __exceptionPreprocess + 182
1 libobjc.A.dylib 0x04648837 objc_exception_throw + 44
2 CoreFoundation 0x049a5a11 -[NSException raise] + 17
3 Foundation 0x022e1c9e -[NSObject(NSKeyValueCoding) setValue:forUndefinedKey:] + 282
4 Foundation 0x0223dce8 _NSSetUsingKeyValueSetter + 115
5 Foundation 0x0223dc6d -[NSObject(NSKeyValueCoding) setValue:forKey:] + 267
6 My_App 0x005c3858 +[DCAttributeSetter assingValue:forAttributeName:andAttributeClass:onObject:] + 345
7 My_App 0x005c138f -[DCKeyValueObjectMapping parseValue:forObject:inAttribute:dictionary:] + 374
8 My_App 0x005c0c30 -[DCKeyValueObjectMapping setValuesOnObject:withDictionary:] + 615
9 My_App 0x005c09a6 -[DCKeyValueObjectMapping parseDictionary:forParentObject:] + 221
10 My_App 0x005c08c4 -[DCKeyValueObjectMapping parseDictionary:] + 48
11 My_App 0x0080eba5 - *** My_App_Code *** + 805
)

Create tag 1.4

Hi there, after the latest modifications you merged from my pull requests, some features need a version bump to be correctly referenced by Cocoa Pods.
Could you create a tag 1.4, please ?

Support NSNull when converting NSDictionary/NSArray to Model

Hi,

I got some issues when converting NSDictionary / NSArray from JSONKit with DCKeyValueObjectMapping library because it does not support NSNull representation.

By default when a object is nil, JSONKit will use NSNull.

I fixed it in DCNSDateConverter, but there may be more cases.

1 - Data loader code (Loads Facebook repositories on GitHub)

Note: Some repositories don't have push date. In this case JSONKit sets NSNull in dictionary when deserializing the data.

AFHTTPClient *httpClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:@"https://api.github.com"]];
NSURLRequest *urlRequest = [httpClient requestWithMethod:@"GET" path:@"/orgs/facebook/repos" parameters:nil];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSString *jsonString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
    NSArray *json = [jsonString objectFromJSONString];

    DCParserConfiguration *parserConfiguration = [DCParserConfiguration configuration];
    parserConfiguration.datePattern = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
    DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: GHRepository.class andConfiguration:parserConfiguration];
    NSLog(@"Data loaded");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[operation start];

2 - GHRepository code

#import "GHOwner.h"

@interface GHRepository : NSObject

@property (nonatomic, strong) NSNumber *identifier;

@property (nonatomic, strong) NSNumber *forks;
@property (nonatomic, strong) NSString *full_name;
@property (nonatomic, strong) NSString *language;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSNumber *open_issues;
@property (nonatomic, strong) NSString *url;
@property (nonatomic, strong) NSNumber *watchers;
@property (nonatomic, strong) NSDate *pushed_at;

@end

3 - Code to fix NSNull in DCNSDateConverter class

- (id)transformValue:(id)value forDynamicAttribute:(DCDynamicAttribute *)attribute {
    BOOL validDouble = [self validDouble:[NSString stringWithFormat:@"%@", value]];
    if(validDouble){
        return [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
    }else{
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = self.pattern;
        /* Code to fix the issue here */
        if ([value isKindOfClass:NSNull.class]) {
            return nil;
        }
        return [formatter dateFromString:value];
    }
}

Support multiple datePattern

Restkit supports multiple datePattern. It would be great to support multiple date patterns as well.

It is mainly needed when parser configurations are merged.

how to parse a json with array at root and no root key defined

I am receiving this kind of response from my server

[
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text"
},
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text"
}
]

How do I parse this ?

Unable to autoassign class value containing "_"

Hello, I use KeyValueObjectMapping for getting data from database.
Everything works pretty well but it doesn't assign value to custom class fields containing the char "_" and this is very strange.
Can this problem be solved? Thanks.

serializeObject with NSArray inside

I have a JSON like your Tweet example - that includes simple attributes and Array of complex type (includes several attributes too).
I'm able to parse it into the object pretty well - my parsed object contains NSArray with items of my custom type.
But when I'm trying to serialize this object back to the JSON string (through the NSDictionary) I have some troubles:
it creates NSDictionary that contains key with array of my custom type and i couldn't convert it into the NSString using NSJSONSerialization (got an exception 'Invalid type in JSON write (MyPhotoModel)')

NSNumber type problem

if the Web Services return the response .

{
"number" : "12345"
}

NSNumber* number - will be parsed as String.

My app crash If I use enum in property

I have model with enum property and server returns the string at same property.
When I try to parse it fails to convert the same string to enum property. Is there any possibility for this ?

Newbie with the pod

Hi all,

I'm having problems to parse a JSON Structure like that:

"data": [
{
  "_id": "5570d8520cf28e92e8f26d1c",
  "imageUrl": "http://bucket.konacloud.io/external/api/bucket/null/papermoon/moons/kf-c30a740a-1fde-4d7e-9b57-ff3a3597192e",
  "photoCoordinates": {
    "topLeft": {
      "x": 246.84910383779814,
      "y": 198.83961701323295
    },
    "topRight": {
      "x": 545.3495307916241,
      "y": 247.7891994494284
    },
    "bottomLeft": {
      "x": 213.34832193248192,
      "y": 403.13140469208895
    },
    "bottomRight": {
      "x": 511.84874888630793,
      "y": 452.0809871282844
    }
  },
  "textCoordinates": {
    "topLeft": {
      "x": 241.8624242050808,
      "y": 8.429022588484031
    },
    "topRight": {
      "x": 544.3380331880693,
      "y": 11.088436558626118
    },
    "bottomLeft": {
      "x": 241.42265371284054,
      "y": 58.44750370747054
    },
    "bottomRight": {
      "x": 543.898262695829,
      "y": 61.10691767761263
    }
  },
  "metadata": {
    "text": {
      "left": 241.86242420508074,
      "top": 8.429022588484031,
      "angle": 0.5037406847670752,
      "width": 302.48729975371583,
      "height": 50.020414347906545
    },
    "photo": {
      "left": 246.8491038377981,
      "top": 198.83961701323295,
      "angle": 9.312760096851482,
      "width": 302.48729975371583,
      "height": 207.020377985575
    }
  },
  "category": {
    "_id": "556cedaf0cf2bfc0f5ba1820",
    "name": "Easter",
    "description": "Easter Moons"
  }
}

]

I have a Moon class defined in this way:

@interface Moon : NSManagedObject

@property (nonatomic, retain) NSNumber * moonId;
@property (nonatomic, retain) NSString * imageUrl;
@property (nonatomic, retain) PMCategory *category;
@property (nonatomic, retain) Coordinates *photoCoordinates;
@property (nonatomic, retain) Coordinates *textCoordinates;

@end

And a the corresponding DCParserConfiguration setup for this class is:

-(DCParserConfiguration*) getMoonsParserConfiguration{

DCParserConfiguration *config = [DCParserConfiguration configuration];

DCObjectMapping *mapId = [DCObjectMapping mapKeyPath:@"moonId" toAttribute:@"_id" onClass:[Moon class]];
DCObjectMapping *mapUrl = [DCObjectMapping mapKeyPath:@"imageUrl" toAttribute:@"imageUrl" onClass:[Moon class]];

DCObjectMapping *categoryToMoon = [DCObjectMapping mapKeyPath:@"category" toAttribute:@"category" onClass:[PMCategory class]];

DCArrayMapping *mapperPhotoCoordinates = [DCArrayMapping mapperForClassElements:[Coordinates class] forAttribute:@"photoCoordinates" onClass:[Moon class]];
DCArrayMapping *mapperTextCoordinates = [DCArrayMapping mapperForClassElements:[Coordinates class] forAttribute:@"textCoordinates" onClass:[Moon class]];
DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements:[PMCategory class] forAttribute:@"category" onClass:[Moon class]];

[config addObjectMapping:mapId];
[config addObjectMapping:mapUrl];
[config addObjectMapping:categoryToMoon];
[config addArrayMapper:mapperPhotoCoordinates];
[config addArrayMapper:mapperTextCoordinates];
[config addArrayMapper:mapper];

return config;
}

I've added the following line to config trying to fix the problem, but I think that shouldn't be there.

 DCObjectMapping *mapUrl = [DCObjectMapping mapKeyPath:@"imageUrl" toAttribute:@"imageUrl" onClass:[Moon class]];

This line is confusing for me, I imagine on my first approach to this library that the mapKeyPath is the server response JSON key and the attribute is the Model attribute, but seems to be that they must go swapped?

 DCObjectMapping *mapId = [DCObjectMapping mapKeyPath:@"moonId" toAttribute:@"_id" onClass:[Moon class]];

Otherwise I have an error validating the key into the DCAttributeSetter class implementation, the line that validates the value fails with the first attribute If the line above is not present. Anyway I couldn't solve the problem at all, now with the second attribute imageUrl (named identically on the hash) is failing on the same line.

if([object validateValue:&value forKey:attributeName error:nil]){

I'm really stuck with this and any help to try to solve my issues is really appreciated.
Best,
Hernan

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.