Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merged in xml-array-parsing branch

  • Loading branch information...
commit 55b8a6a8851dc41bfa50d9e0fbe074628b8cbf20 2 parents efe72a6 + b323ff5
@sleroux sleroux authored
View
14 Mantle.xcodeproj/project.pbxproj
@@ -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
3  Mantle/MTLReflection.h
@@ -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
8 Mantle/MTLReflection.m
@@ -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
22 Mantle/MTLXMLAdapter.h
@@ -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
177 Mantle/MTLXMLAdapter.m
@@ -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
19 Mantle/MTLXMLElement.h
@@ -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
View
136 Mantle/MTLXMLElement.m
@@ -0,0 +1,136 @@
+//
+// MTLXMLElement.m
+// Mantle
+//
+// Created by Stephan Leroux on 2014-05-29.
+// Copyright (c) 2014 GitHub. All rights reserved.
+//
+
+#import "MTLXMLElement.h"
+
+#import <libxml2/libxml/xmlreader.h>
+#import <libxml2/libxml/xmlmemory.h>
+#import <libxml/xpath.h>
+#import <libxml/xpathInternals.h>
+
+@interface MTLXMLElement () {
+ xmlDocPtr doc_;
+ xmlNodePtr node_;
+}
+
+@end
+
+@implementation MTLXMLElement
+
+- (id)initWithData:(NSData *)data
+{
+ if (self = [super init]) {
+ doc_ = xmlReadMemory([data bytes], (int)[data length], "", nil, XML_PARSE_RECOVER);
+ node_ = xmlDocGetRootElement(doc_);
+ }
+
+ return self;
+}
+
+- (id)initWithXMLDoc:(xmlDocPtr)doc node:(xmlNodePtr)node
+{
+ if (self = [super init]) {
+ doc_ = doc;
+ node_ = node;
+ }
+ return self;
+}
+
+- (NSString *)value
+{
+ NSString *valueStr;
+
+ switch (node_->type) {
+ case XML_ELEMENT_NODE: {
+ xmlChar *key = xmlNodeGetContent(node_);
+ valueStr = (key ? [NSString stringWithUTF8String:(const char *)key] : @"");
+ xmlFree(key);
+ break;
+ }
+
+ case XML_ATTRIBUTE_NODE: {
+ if (node_->children->content != NULL) {
+ valueStr = [NSString stringWithUTF8String:(const char *)node_->children->content];
+ } else {
+ valueStr = nil;
+ }
+
+ break;
+ }
+
+ default:
+ valueStr = nil;
+ }
+
+ return valueStr;
+}
+
+- (NSArray *)xpathQuery:(NSString *)xpath error:(NSError **)error
+{
+ NSParameterAssert(xpath);
+
+ xmlXPathContextPtr context = xmlXPathNewContext(doc_);
+
+ if (context == NULL) {
+ return nil;
+ }
+
+ xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *)[xpath cStringUsingEncoding:NSUTF8StringEncoding],
+ context);
+ if(object == NULL) {
+ return nil;
+ }
+
+ xmlNodeSetPtr nodes = object->nodesetval;
+ if (nodes == NULL) {
+ return nil;
+ }
+
+ NSMutableArray *resultNodes = [NSMutableArray array];
+
+ for (NSInteger i = 0; i < nodes->nodeNr; i++) {
+ xmlNodePtr node = nodes->nodeTab[i];
+ id result;
+
+ switch (node->type) {
+ case XML_ATTRIBUTE_NODE: {
+ result = [[MTLXMLElement alloc] initWithXMLDoc:doc_ node:node];
+ break;
+ }
+
+ case XML_ELEMENT_NODE: {
+ NSString *nodeAsString = [self nodePtrToString:node];
+ result = [[MTLXMLElement alloc] initWithData:[nodeAsString dataUsingEncoding:NSUTF8StringEncoding]];
+ break;
+ }
+
+ default:
+ result = [[MTLXMLElement alloc] init];
+ break;
+ }
+
+ [resultNodes addObject:result];
+ }
+
+ xmlXPathFreeObject(object);
+ xmlXPathFreeContext(context);
+
+ return resultNodes;
+}
+
+- (NSString *)nodePtrToString:(xmlNodePtr)nodePtr
+{
+ xmlBufferPtr buffer = xmlBufferCreate();
+ xmlNodeDump(buffer, doc_, nodePtr, 0, 1);
+ NSString *objcString = [NSString stringWithUTF8String:(const char *)buffer->content];
+ xmlBufferFree(buffer);
+ return objcString;
+}
+
+@end
+
View
23 Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.h
@@ -44,6 +44,29 @@ extern NSString * const MTLBooleanValueTransformerName;
// array elements back and forth.
+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass;
+// Creates a reversible transformer to convert a MTLXMLElement into a MTLModel
+// object, and vice-versa.
+//
+// modelClass - The MTLModel subclass to attempt to parse from the MTLXMLElement. This
+// class must conform to <MTLXMLSerializing>. This argument must
+// not be nil.
+//
+// Returns a reversible transformer which uses MTLXMLAdapter for transforming
+// values back and forth.
++ (NSValueTransformer *)mtl_XMLDocumentTransformerWithModelClass:(Class)modelClass;
+
+
+// Creates a reversible transformer to convert an array of MTLXMLElement
+// into an array of MTLModel objects, and vice-versa.
+//
+// modelClass - The MTLModel subclass to attempt to parse from each JMTLXMLElement
+// . This class must conform to <MTLXMLSerializing>.
+// This argument must not be nil.
+//
+// Returns a reversible transformer which uses MTLXMLAdapter for transforming
+// array elements back and forth.
++ (NSValueTransformer *)mtl_XMLArrayTransformerWithModelClass:(Class)modelClass;
+
// A reversible value transformer to transform between the keys and objects of a
// dictionary.
//
View
26 Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m
@@ -8,8 +8,10 @@
#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h"
#import "MTLJSONAdapter.h"
+#import "MTLXMLAdapter.h"
#import "MTLModel.h"
#import "MTLValueTransformer.h"
+#import "MTLXMLElement.h"
NSString * const MTLURLValueTransformerName = @"MTLURLValueTransformerName";
NSString * const MTLBooleanValueTransformerName = @"MTLBooleanValueTransformerName";
@@ -116,6 +118,30 @@ + (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass
}];
}
++ (NSValueTransformer *)mtl_XMLDocumentTransformerWithModelClass:(Class)modelClass
+{
+ return [MTLValueTransformer transformerWithBlock:^ id (id element) {
+ NSAssert([element isKindOfClass:MTLXMLElement.class], @"XML Document transformer requires a MTLXMLElement has input");
+ return [MTLXMLAdapter modelOfClass:modelClass fromXMLElement:element error:NULL];
+ }];
+}
+
++ (NSValueTransformer *)mtl_XMLArrayTransformerWithModelClass:(Class)modelClass
+{
+ return [MTLValueTransformer transformerWithBlock:^id (id elements) {
+ NSAssert([elements isKindOfClass:NSArray.class], @"Array XML transformer expects array as input");
+
+ NSMutableArray *models = [NSMutableArray array];
+ for (id element in elements) {
+ NSAssert([element isKindOfClass:MTLXMLElement.class], @"XML array must only use MTLXMLElements");
+ id model = [MTLXMLAdapter modelOfClass:modelClass fromXMLElement:element error:NULL];
+ [models addObject:model];
+ }
+
+ return models;
+ }];
+}
+
+ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary {
return [self mtl_valueMappingTransformerWithDictionary:dictionary defaultValue:nil reverseDefaultValue:nil];
}
View
52 MantleTests/MTLPredefinedTransformerAdditionsSpec.m
@@ -7,6 +7,8 @@
//
#import "MTLTestModel.h"
+#import "MTLTestXMLModel.h"
+#import "MTLXMLElement.h"
enum : NSInteger {
MTLPredefinedTransformerAdditionsSpecEnumNegative = -1,
@@ -124,6 +126,56 @@
});
});
+describe(@"XML Transformers", ^{
+ describe(@"MTLXMLElement transformer", ^{
+ __block NSValueTransformer *transformer;
+ __block MTLTestXMLModel *model;
+ __block MTLXMLElement *rootElement;
+
+ const NSString *modelXML = @"<model><username>first</username></model>";
+
+ beforeEach(^{
+ rootElement = [[MTLXMLElement alloc] initWithData:[modelXML dataUsingEncoding:NSUTF8StringEncoding]];
+ model = [[MTLTestXMLModel alloc] init];
+ model.name = @"first";
+ transformer = [NSValueTransformer mtl_XMLDocumentTransformerWithModelClass:MTLTestXMLModel.class];
+ expect(transformer).notTo.beNil();
+ });
+
+ it(@"should transform a MTLXMLElement into a model", ^{
+ expect([transformer transformedValue:rootElement]).to.equal(model);
+ });
+ });
+
+ describe(@"Array of MTLXMLElement representation", ^{
+ __block NSValueTransformer *transformer;
+ __block NSArray *elements;
+ __block NSArray *models;
+
+ const NSString *modelsXML = @"<models><model><username>first</username></model><model><username>second</username></model></models>";
+
+ beforeEach(^{
+ MTLXMLElement *element = [[MTLXMLElement alloc] initWithData:[modelsXML dataUsingEncoding:NSUTF8StringEncoding]];
+ elements = [element xpathQuery:@"/models/model" error:NULL];
+
+ MTLTestXMLModel *firstModel = [[MTLTestXMLModel alloc] init];
+ firstModel.name = @"first";
+
+ MTLTestXMLModel *secondModel = [[MTLTestXMLModel alloc] init];
+ secondModel.name = @"second";
+
+ models = @[firstModel, secondModel];
+
+ transformer = [NSValueTransformer mtl_XMLArrayTransformerWithModelClass:MTLTestXMLModel.class];
+ expect(transformer).notTo.beNil();
+ });
+
+ it(@"should transform an array of MTLXMLElements into an array of models", ^{
+ expect([transformer transformedValue:elements]).to.equal(models);
+ });
+ });
+});
+
describe(@"value mapping transformer", ^{
__block NSValueTransformer *transformer;
View
8 MantleTests/MTLXMLAdapterSpec.m
@@ -38,7 +38,7 @@
NSData *xmlData = [values dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
- MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromXML:xmlData error:&error];
+ MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromData:xmlData error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
@@ -52,7 +52,7 @@
NSData *xmlData = [values dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
- MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromXML:xmlData error:&error];
+ MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromData:xmlData error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
@@ -65,7 +65,7 @@
NSData *xmlData = [values dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
- MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromXML:xmlData error:&error];
+ MTLTestXMLModel *model = [MTLXMLAdapter modelOfClass:MTLTestXMLModel.class fromData:xmlData error:&error];
expect(model).notTo.beNil();
expect(error).to.beNil();
@@ -77,7 +77,7 @@
NSData *xmlData = [values dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
- MTLIllegalXMLMappingModel *model = [MTLXMLAdapter modelOfClass:MTLIllegalXMLMappingModel.class fromXML:xmlData error:&error];
+ MTLIllegalXMLMappingModel *model = [MTLXMLAdapter modelOfClass:MTLIllegalXMLMappingModel.class fromData:xmlData error:&error];
expect(model).beNil();
expect(error).notTo.beNil();
expect(error.domain).to.equal(MTLXMLAdapterErrorDomain);
Please sign in to comment.
Something went wrong with that request. Please try again.