Skip to content

Improved resource parsing #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 31 additions & 25 deletions Classes/JSONAPI.m
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary {
// Parse errors
if (dictionary[@"errors"]) {
NSMutableArray *errors = [[NSMutableArray alloc] init];
NSLog(@"ERRORS - %@", dictionary[@"errors"]);
// NSLog(@"ERRORS - %@", dictionary[@"errors"]);
for (NSDictionary *data in dictionary[@"errors"]) {

JSONAPIErrorResource *resource = [[JSONAPIErrorResource alloc] initWithDictionary: data];
NSLog(@"Error resource - %@", resource);
// NSLog(@"Error resource - %@", resource);
if (resource) [errors addObject:resource];
}
_errors = errors;
Expand All @@ -171,32 +171,38 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary {

- (void)inflateWithResource:(NSObject <JSONAPIResource> *)resource
{
NSMutableArray *resourceArray = [[NSMutableArray alloc] init];
[resourceArray addObject:resource];
_resources = resourceArray;
_resources = @[resource];

NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource];

newDictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource];

NSArray *included = [JSONAPIResourceParser relatedResourcesFor:resource];
if (included.count) {
newDictionary[@"included"] = included;

NSMutableDictionary *includedResources = [[NSMutableDictionary alloc] init];
for (NSObject <JSONAPIResource> *linked in included) {

JSONAPIResourceDescriptor *desc = [[linked class] descriptor];

NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy;
typeDict[linked.ID] = resource;

includedResources[desc.type] = typeDict;
}
_includedResources = includedResources;
NSArray *relatedResources = [JSONAPIResourceParser relatedResourcesFor:resource];
if (relatedResources.count) {
_includedResources = [self mapIncludedResources:relatedResources forResource:resource];
dictionary[@"included"] = [self parseRelatedResources:relatedResources];
}

_dictionary = newDictionary;
_dictionary = dictionary;
}

- (NSDictionary *)mapIncludedResources:(NSArray *)relatedResources forResource:(NSObject <JSONAPIResource> *)resource
{
NSMutableDictionary *includedResources = [NSMutableDictionary new];
for (NSObject <JSONAPIResource> *linked in relatedResources) {
JSONAPIResourceDescriptor *desc = [[linked class] descriptor];
NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy;
typeDict[linked.ID] = resource;
includedResources[desc.type] = typeDict;
}
return includedResources;
}

- (NSArray *)parseRelatedResources:(NSArray *)relatedResources
{
NSMutableArray *parsedResources = [NSMutableArray new];
for (NSObject <JSONAPIResource> *linked in relatedResources) {
[parsedResources addObject:[JSONAPIResourceParser dictionaryFor:linked]];
}
return parsedResources;
}

@end
10 changes: 10 additions & 0 deletions Classes/JSONAPIResourceDescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
*/
@property (strong) NSString *idProperty;

@property (strong) NSString *selfLinkProperty;

/**
* JSON-API "id" optional format.
*
Expand Down Expand Up @@ -95,6 +97,14 @@
*/
- (void)addProperty:(NSString*)name;

/**
* Add a simple property with custom json name.
*
* @param name The name of the property in the class.
* @param jsonName The label of the property in JSON.
*/
- (void)addProperty:(NSString*)name withJsonName:(NSString *)json;

/**
* Add a simple property with custom transform object.
*
Expand Down
5 changes: 5 additions & 0 deletions Classes/JSONAPIResourceDescriptor.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ - (void)addProperty:(NSString*)name {
[self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:name]];
}

- (void)addProperty:(NSString*)name withJsonName:(NSString *)json
{
[self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:json]];
}

- (void)addProperty:(NSString*)name withDescription:(JSONAPIPropertyDescriptor*)description {
[[self properties] setValue:description forKey:name];
}
Expand Down
17 changes: 9 additions & 8 deletions Classes/JSONAPIResourceParser.m
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ + (void)set:(NSObject <JSONAPIResource> *)resource withDictionary:dictionary {

NSDictionary *relationships = [dictionary objectForKey:@"relationships"];
NSDictionary *attributes = [dictionary objectForKey:@"attributes"];
NSDictionary *links = [dictionary objectForKey:@"links"];

id ID = [dictionary objectForKey:@"id"];
NSFormatter *format = [descriptor idFormatter];
Expand All @@ -191,6 +192,11 @@ + (void)set:(NSObject <JSONAPIResource> *)resource withDictionary:dictionary {
} else {
[resource setValue:ID forKey:[descriptor idProperty]];
}

if (descriptor.selfLinkProperty) {
NSString *selfLink = links[@"self"];
[resource setValue:selfLink forKey:descriptor.selfLinkProperty];
}

// Loops through all keys to map to properties
NSDictionary *properties = [descriptor properties];
Expand Down Expand Up @@ -332,20 +338,15 @@ + (NSArray*)relatedResourcesFor:(NSObject <JSONAPIResource>*)resource {
NSDictionary *properties = [descriptor properties];
for (NSString *key in properties) {
JSONAPIPropertyDescriptor *property = [properties objectForKey:key];

if (property.resourceType) {
id value = [self valueForKey:key];
id value = [resource valueForKey:key];
if ([value isKindOfClass:[NSArray class]]) {
for (NSObject <JSONAPIResource> *element in value) {
[related addObject:[JSONAPIResourceParser dictionaryFor:element]];
}
[related addObjectsFromArray:value];
} else {
NSObject <JSONAPIResource> *attribute = value;
[related addObject:[JSONAPIResourceParser dictionaryFor:attribute]];
[related addObject:value];
}
}
}

return related;
}

Expand Down
1 change: 1 addition & 0 deletions Project/JSONAPI/ArticleResource.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ + (JSONAPIResourceDescriptor*)descriptor {
__descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"articles"];

[__descriptor setIdProperty:@"ID"];
[__descriptor setSelfLinkProperty:@"selfLink"];

[__descriptor addProperty:@"title"];
[__descriptor addProperty:@"date"
Expand Down
2 changes: 1 addition & 1 deletion Project/JSONAPI/CommentResource.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ + (JSONAPIResourceDescriptor*)descriptor {

[__descriptor setIdProperty:@"ID"];

[__descriptor addProperty:@"text" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"body"]];
[__descriptor addProperty:@"text" withJsonName:@"body"];

[__descriptor hasOne:[PeopleResource class] withName:@"author"];
});
Expand Down
2 changes: 1 addition & 1 deletion Project/JSONAPI/PeopleResource.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ + (JSONAPIResourceDescriptor*)descriptor {
[__descriptor setIdProperty:@"ID"];

[__descriptor addProperty:@"firstName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"first-name"]];
[__descriptor addProperty:@"lastName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"last-name"]];
[__descriptor addProperty:@"lastName" withJsonName:@"last-name"];
[__descriptor addProperty:@"twitter"];
});

Expand Down
29 changes: 20 additions & 9 deletions Project/JSONAPITests/JSONAPITests.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "JSONAPIResourceDescriptor.h"
#import "JSONAPIErrorResource.h"
#import "JSONAPIResourceParser.h"
#import "NSDateFormatter+JSONAPIDateFormatter.h"

#import "CommentResource.h"
#import "PeopleResource.h"
Expand Down Expand Up @@ -65,10 +66,12 @@ - (void)testDataArticles {
ArticleResource *article = jsonAPI.resource;
XCTAssert([article isKindOfClass:[ArticleResource class]], @"Article should be a ArticleResource");
XCTAssertEqualObjects(article.ID, @"1", @"Article id should be 1");
XCTAssertTrue([article.selfLink isEqualToString:@"http://example.com/articles/1"], @"Article selfLink should be 'http://example.com/articles/1'");
XCTAssertEqualObjects(article.title, @"JSON API paints my bikeshed!", @"Article title should be 'JSON API paints my bikeshed!'");

NSArray *dateStrings = @[@"2015-09-01T12:15:00Z",@"2015-08-01T06:15:00Z"];
XCTAssertEqualObjects(article.versions, dateStrings, @"Article versions should contain an array of date strings");
NSArray *dates = @[[[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-09-01T12:15:00.000Z"],
[[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-08-01T06:15:00.000Z"]];
XCTAssertEqualObjects(article.versions, dates, @"Article versions should contain an array of date objects");
}

- (void)testIncludedPeopleAndComments {
Expand Down Expand Up @@ -181,13 +184,21 @@ - (void)testSerializeComplex {
}

- (void)testCreate {
PeopleResource *newAuthor = [[PeopleResource alloc] init];

newAuthor.firstName = @"Karl";
newAuthor.lastName = @"Armstrong";

JSONAPI *jsonAPI = [JSONAPI jsonAPIWithResource:newAuthor];
XCTAssertEqualObjects([jsonAPI dictionary][@"data"][@"type"], @"people", @"Did not create person!");
NSDictionary *json = [self mainExampleJSON];
JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json];

ArticleResource *article = jsonAPI.resource;

jsonAPI = [JSONAPI jsonAPIWithResource:article];
NSDictionary *dictionary = [jsonAPI dictionary];
XCTAssertEqualObjects(dictionary[@"data"][@"type"], @"articles", @"Did not create article!");
XCTAssertEqualObjects(dictionary[@"data"][@"attributes"][@"title"], @"JSON API paints my bikeshed!", @"Did not parse title!");
XCTAssertEqual([dictionary[@"data"][@"relationships"][@"comments"][@"data"] count], 2, @"Did not parse relationships!");
XCTAssertEqual([dictionary[@"included"] count], 3, @"Did not parse included resources!");
XCTAssertEqualObjects(dictionary[@"included"][0][@"type"], @"people", @"Did not parse included people object!");
XCTAssertEqualObjects(dictionary[@"included"][0][@"id"], @"9", @"Did not parse ID!");
XCTAssertEqualObjects(dictionary[@"included"][1][@"type"], @"comments", @"Did not parse included comments object!");
XCTAssertEqualObjects(dictionary[@"included"][1][@"relationships"][@"author"][@"data"][@"type"], @"people", @"Did not parse included comments author!");
}

#pragma mark - Generic relationships tests
Expand Down
4 changes: 2 additions & 2 deletions Project/JSONAPITests/main_example.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"attributes": {
"title": "JSON API paints my bikeshed!",
"versions": [
"2015-09-01T12:15:00Z",
"2015-08-01T06:15:00Z"
"2015-09-01T12:15:00.000Z",
"2015-08-01T06:15:00.000Z"
]
},
"relationships": {
Expand Down