Permalink
Browse files

Merged in xml-array-parsing branch

  • Loading branch information...
sleroux committed May 30, 2014
2 parents efe72a6 + b323ff5 commit 55b8a6a8851dc41bfa50d9e0fbe074628b8cbf20
@@ -139,10 +139,13 @@
E93E47A41936304E006B0D66 /* MTLXMLAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = E93E479719362EF9006B0D66 /* MTLXMLAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; };
E93E47A6193630D3006B0D66 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E93E47A5193630D3006B0D66 /* libz.dylib */; };
E93E47A8193630DB006B0D66 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E93E47A7193630DB006B0D66 /* libxml2.dylib */; };
- E93E47AA193632F7006B0D66 /* libRaptureXML.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E93E47A9193632F7006B0D66 /* libRaptureXML.a */; };
E93E47AB19363303006B0D66 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E93E47A7193630DB006B0D66 /* libxml2.dylib */; };
E93E47B0193637AB006B0D66 /* MTLTestXMLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E93E47AD1936373B006B0D66 /* MTLTestXMLModel.m */; };
E93E47B1193637AC006B0D66 /* MTLTestXMLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E93E47AD1936373B006B0D66 /* MTLTestXMLModel.m */; };
+ E97B9D291937CF2800AB56BA /* MTLXMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E97B9D271937CF2800AB56BA /* MTLXMLElement.h */; };
+ E97B9D2A1937CF2800AB56BA /* MTLXMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = E97B9D281937CF2800AB56BA /* MTLXMLElement.m */; };
+ E97B9D2B1937CFC800AB56BA /* MTLXMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = E97B9D281937CF2800AB56BA /* MTLXMLElement.m */; };
+ E97B9D2C1937CFC800AB56BA /* MTLXMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = E97B9D281937CF2800AB56BA /* MTLXMLElement.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -260,6 +263,8 @@
E93E47A9193632F7006B0D66 /* libRaptureXML.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libRaptureXML.a; path = "Mantle/rapturexml/build/Debug-iphoneos/libRaptureXML.a"; sourceTree = "<group>"; };
E93E47AC1936373B006B0D66 /* MTLTestXMLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLTestXMLModel.h; sourceTree = "<group>"; };
E93E47AD1936373B006B0D66 /* MTLTestXMLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLTestXMLModel.m; sourceTree = "<group>"; };
+ E97B9D271937CF2800AB56BA /* MTLXMLElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLXMLElement.h; sourceTree = "<group>"; };
+ E97B9D281937CF2800AB56BA /* MTLXMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLXMLElement.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -298,7 +303,6 @@
buildActionMask = 2147483647;
files = (
E93E47AB19363303006B0D66 /* libxml2.dylib in Frameworks */,
- E93E47AA193632F7006B0D66 /* libRaptureXML.a in Frameworks */,
D021D947170F8A8100C37E36 /* CoreData.framework in Frameworks */,
D042FC8815F72BC7004E8054 /* SenTestingKit.framework in Frameworks */,
D042FC8B15F72BC7004E8054 /* Foundation.framework in Frameworks */,
@@ -446,6 +450,8 @@
D058FE1E16EFB3D2009DFB47 /* MTLReflection.m */,
D01BD0AB16CB46B600EC95C7 /* Adapters */,
D01BD0AC16CB46BD00EC95C7 /* Value Transformers */,
+ E97B9D271937CF2800AB56BA /* MTLXMLElement.h */,
+ E97B9D281937CF2800AB56BA /* MTLXMLElement.m */,
);
name = Modules;
sourceTree = "<group>";
@@ -584,6 +590,7 @@
buildActionMask = 2147483647;
files = (
D0760E0515FFBD440060F550 /* Mantle.h in Headers */,
+ E97B9D291937CF2800AB56BA /* MTLXMLElement.h in Headers */,
D0760E7815FFBF330060F550 /* MTLModel.h in Headers */,
D08B5AAE16002694001FE685 /* MTLValueTransformer.h in Headers */,
88080C18160A706900CCABF2 /* NSArray+MTLManipulationAdditions.h in Headers */,
@@ -790,6 +797,7 @@
files = (
D0760E7915FFBF330060F550 /* MTLModel.m in Sources */,
D08B5AAF16002694001FE685 /* MTLValueTransformer.m in Sources */,
+ E97B9D2A1937CF2800AB56BA /* MTLXMLElement.m in Sources */,
88080C19160A706900CCABF2 /* NSArray+MTLManipulationAdditions.m in Sources */,
D0C27D0B16110973002FE587 /* NSDictionary+MTLManipulationAdditions.m in Sources */,
E93E479A19362EF9006B0D66 /* MTLXMLAdapter.m in Sources */,
@@ -837,6 +845,7 @@
files = (
D0760E7A15FFBF330060F550 /* MTLModel.m in Sources */,
D08B5AB016002694001FE685 /* MTLValueTransformer.m in Sources */,
+ E97B9D2B1937CFC800AB56BA /* MTLXMLElement.m in Sources */,
88080C1A160A706900CCABF2 /* NSArray+MTLManipulationAdditions.m in Sources */,
D0C27D0C16110973002FE587 /* NSDictionary+MTLManipulationAdditions.m in Sources */,
E93E47A119363043006B0D66 /* MTLXMLAdapter.m in Sources */,
@@ -891,6 +900,7 @@
D0A56B841804B16B00A84EDC /* NSObject+MTLComparisonAdditions.m in Sources */,
D0A56B861804B16B00A84EDC /* NSValueTransformer+MTLInversionAdditions.m in Sources */,
D0A56B821804B16B00A84EDC /* NSDictionary+MTLManipulationAdditions.m in Sources */,
+ E97B9D2C1937CFC800AB56BA /* MTLXMLElement.m in Sources */,
D0A56B781804B14F00A84EDC /* MTLModel+NSCoding.m in Sources */,
D0A56B7A1804B15600A84EDC /* MTLJSONAdapter.m in Sources */,
D0A56B7C1804B15600A84EDC /* MTLManagedObjectAdapter.m in Sources */,
View
@@ -29,3 +29,6 @@ SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((
// Returns a selector, or NULL if the input strings cannot form a valid
// selector.
SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3)));
+
+
+Class MTLClassForPropertyOfClass(NSString *propertyName, Class modelClass);
View
@@ -7,6 +7,7 @@
//
#import "MTLReflection.h"
+#import "EXTRuntimeExtensions.h"
#import <objc/runtime.h>
SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) {
@@ -48,3 +49,10 @@ SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, cons
return sel_registerName(selector);
}
+
+Class MTLClassForPropertyOfClass(NSString *propertyName, Class modelClass)
+{
+ objc_property_t property = class_getProperty(modelClass, [propertyName UTF8String]);
+ mtl_propertyAttributes *mtlPropertyAttributes = mtl_copyPropertyAttributes(property);
+ return mtlPropertyAttributes->objectClass;
+}
View
@@ -1,14 +1,6 @@
-//
-// MTLXMLAdapter.h
-// TSN Hockey
-//
-// Created by Stephan Leroux on 2014-05-28.
-// Copyright (c) 2014 TSN. All rights reserved.
-//
-
#import <Foundation/Foundation.h>
-@class MTLModel;
+@class MTLModel, MTLXMLElement;
@protocol MTLXMLSerializable <NSObject>
@@ -37,9 +29,17 @@ extern const NSInteger MTLXMLAdapterErrorInvalidRXMLElement;
@property (strong, nonatomic, readonly) MTLModel *model;
+ (id)modelOfClass:(Class)modelClass
- fromXML:(NSData *)xml
+ fromData:(NSData *)xml
+ error:(NSError **)error;
+
++ (id)modelOfClass:(Class)modelClass
+ fromXMLElement:(MTLXMLElement *)xml
error:(NSError **)error;
-- (id)initWithXMLData:(NSData *)xml modelClass:(Class)modelClass error:(NSError **)error;
+- (id)initWithModel:(MTLModel<MTLXMLSerializable> *)model;
+
+- (id)initWithXMLData:(NSData *)data modelClass:(Class)modelClass error:(NSError **)error;
+
+- (id)initWithXMLElement:(MTLXMLElement *)xmlDoc modelClass:(Class)modelClass error:(NSError **)error;
@end
View
@@ -1,20 +1,11 @@
-//
-// MTLXMLAdapter.m
-// TSN Hockey
-//
-// Created by Stephan Leroux on 2014-05-28.
-// Copyright (c) 2014 TSN. All rights reserved.
-//
-
#import "MTLXMLAdapter.h"
#import "MTLModel.h"
#import "MTLReflection.h"
+#import "MTLXMLElement.h"
-#import <libxml2/libxml/xmlreader.h>
-#import <libxml2/libxml/xmlmemory.h>
-#import <libxml/xpath.h>
-#import <libxml/xpathInternals.h>
+#import <objc/runtime.h>
+#import "EXTRuntimeExtensions.h"
NSString * const MTLXMLAdapterErrorDomain = @"MTLXMLAdapterErrorDomain";
const NSInteger MTLXMLAdapterErrorNoClassFound = 2;
@@ -23,145 +14,57 @@
const NSInteger MTLXMLAdapterErrorNoResultXPath = 5;
const NSInteger MTLXMLAdapterErrorInvalidData = 6;
-@interface MTLXMLDocument : NSObject {
- xmlDocPtr doc_;
- xmlNodePtr node_;
-}
+@interface MTLXMLAdapter ()
-- (id)initWithData:(NSData *)data;
+@property (strong, nonatomic, readonly) Class modelClass;
+@property (copy, nonatomic, readonly) NSDictionary *XMLXPathsByPropertyKey;
@end
-@implementation MTLXMLDocument
+@implementation MTLXMLAdapter
-- (id)initWithData:(NSData *)data
++ (id)modelOfClass:(Class)modelClass
+ fromData:(NSData *)xml
+ error:(NSError **)error
{
- if (self = [super init]) {
- doc_ = xmlReadMemory([data bytes], (int)[data length], "", nil, XML_PARSE_RECOVER);
- node_ = xmlDocGetRootElement(doc_);
- }
-
- return self;
+ MTLXMLAdapter *adapter = [[self alloc] initWithXMLData:xml modelClass:modelClass error:error];
+ return adapter.model;
}
-- (id)initWithXMLDoc:(xmlDocPtr)doc node:(xmlNodePtr)node
++ (id)modelOfClass:(Class)modelClass
+ fromXMLElement:(MTLXMLElement *)xml
+ error:(NSError **)error
{
- if (self = [super init]) {
- doc_ = doc;
- node_ = node;
- }
- return self;
-}
-
-- (NSString *)text {
- xmlChar *key = xmlNodeGetContent(node_);
- NSString *text = (key ? [NSString stringWithUTF8String:(const char *)key] : @"");
- xmlFree(key);
-
- return text;
+ MTLXMLAdapter *adapter = [[self alloc] initWithXMLElement:xml modelClass:modelClass error:error];
+ return adapter.model;
}
-- (NSArray *)xpathQuery:(NSString *)xpath error:(NSError **)error
+- (id)initWithModel:(MTLModel<MTLXMLSerializable> *)model
{
- NSParameterAssert(xpath);
+ NSParameterAssert(model != nil);
- xmlXPathContextPtr context = xmlXPathNewContext(doc_);
+ self = [super init];
+ if (self == nil) return nil;
- if (context == NULL) {
- return nil;
- }
+ _model = model;
+ _modelClass = model.class;
+ _XMLXPathsByPropertyKey = [[model.class XMLXPathsByPropertyKey] copy];
- xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *)[xpath cStringUsingEncoding:NSUTF8StringEncoding],
- context);
- if(object == NULL) {
- return nil;
- }
-
- xmlNodeSetPtr nodes = object->nodesetval;
- if (nodes == NULL) {
- return nil;
- }
-
- if (nodes->nodeNr > 1) {
- if (error != NULL) {
- NSString *errorReason = [NSString stringWithFormat:NSLocalizedString(@"XPath query: %@ returns ambiguous results. Make sure that the XPath query returns only one result.", nil),
- NSStringFromClass([self class])];
-
- NSDictionary *userInfo = @{
- NSLocalizedDescriptionKey: NSLocalizedString(@"Ambiguous XPath", @""),
- NSLocalizedFailureReasonErrorKey: errorReason
- };
-
- *error = [NSError errorWithDomain:MTLXMLAdapterErrorDomain code:MTLXMLAdapterErrorAmbiguousXPath
- userInfo:userInfo];
- }
-
- return nil;
- }
-
- NSMutableArray *resultNodes = [NSMutableArray array];
-
- for (NSInteger i = 0; i < nodes->nodeNr; i++) {
- MTLXMLDocument *element = [[MTLXMLDocument alloc] initWithXMLDoc:doc_ node:nodes->nodeTab[i]];
-
- if (element != NULL) {
- [resultNodes addObject:element];
- }
- }
-
- xmlXPathFreeObject(object);
- xmlXPathFreeContext(context);
-
- return resultNodes;
+ return self;
}
-@end
-
-@interface MTLXMLAdapter ()
-
-@property (strong, nonatomic, readonly) Class modelClass;
-@property (copy, nonatomic, readonly) NSDictionary *XMLXPathsByPropertyKey;
-
-@end
-
-@implementation MTLXMLAdapter
-
-+ (id)modelOfClass:(Class)modelClass
- fromXML:(NSData *)xml
- error:(NSError **)error
+- (id)initWithXMLData:(NSData *)data modelClass:(Class)modelClass error:(NSError **)error
{
- MTLXMLAdapter *adapter = [[self alloc] initWithXMLData:xml modelClass:modelClass error:error];
- return adapter.model;
+ return [self initWithXMLElement:[[MTLXMLElement alloc] initWithData:data] modelClass:modelClass error:error];
}
-- (id)initWithXMLData:(NSData *)xml modelClass:(Class)modelClass error:(NSError **)error
+- (id)initWithXMLElement:(MTLXMLElement *)xmlDoc modelClass:(Class)modelClass error:(NSError **)error
{
NSParameterAssert(modelClass != nil);
NSParameterAssert([modelClass isSubclassOfClass:MTLModel.class]);
-
- if (xml == nil || ![xml isKindOfClass:NSData.class]) {
- if (error != NULL) {
- NSString *errorReason = [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an "
- "invalid NSData instance was provided: %@",
- @""),
- NSStringFromClass(modelClass), xml.class];
-
- NSDictionary *userInfo = @{
- NSLocalizedDescriptionKey: NSLocalizedString(@"Missing NSData", @""),
- NSLocalizedFailureReasonErrorKey: errorReason
- };
-
- *error = [NSError errorWithDomain:MTLXMLAdapterErrorDomain code:MTLXMLAdapterErrorInvalidData
- userInfo:userInfo];
- }
-
- return nil;
- }
if(!(self = [super init])) return nil;
- MTLXMLDocument *xmlDoc = [[MTLXMLDocument alloc] initWithData:xml];
-
NSMutableDictionary *dictionaryValue = [NSMutableDictionary dictionary];
_modelClass = modelClass;
@@ -189,13 +92,27 @@ - (id)initWithXMLData:(NSData *)xml modelClass:(Class)modelClass error:(NSError
if (XMLXPath == nil) continue;
NSArray *xpathResults = [xmlDoc xpathQuery:XMLXPath error:error];
- if (*error) return nil;
+ if (xpathResults.count == 0) continue;
+ if (error != NULL && *error) return nil;
- id value;
- if (xpathResults.count > 0) {
- value = [xpathResults[0] text];
+ Class propertyClass = MTLClassForPropertyOfClass(propertyKey, modelClass);
+ id value;
+ // If we are binding the value to an array, send the list of results. If not, just send the first one as
+ // the model expects a single value to be returned from the xpath query
+ if ([propertyClass isSubclassOfClass:NSArray.class]) {
+ value = xpathResults;
} else {
- value = nil;
+ if (xpathResults.count > 1 && error != NULL) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Ambigious XPath", nil),
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%1$@ could not be parsed because the XPath mapping (%@) is ambigious (returned %d results)", nil), propertyKey, modelClass]
+ };
+
+ *error = [NSError errorWithDomain:MTLXMLAdapterErrorDomain code:MTLXMLAdapterErrorAmbiguousXPath userInfo:userInfo];
+ return nil;
+ }
+
+ value = [xpathResults[0] value];
}
if (value == nil) continue;
View
@@ -0,0 +1,19 @@
+//
+// MTLXMLElement.h
+// Mantle
+//
+// Created by Stephan Leroux on 2014-05-29.
+// Copyright (c) 2014 GitHub. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface MTLXMLElement : NSObject
+
+- (id)initWithData:(NSData *)data;
+
+- (NSString *)value;
+
+- (NSArray *)xpathQuery:(NSString *)xpath error:(NSError **)error;
+
+@end
Oops, something went wrong.

0 comments on commit 55b8a6a

Please sign in to comment.