Skip to content

Commit fae5b21

Browse files
authored
Merge pull request fullstackreact#88 from rajchourasia/xmldata
Handling XML response data in IOS
2 parents 00544ee + d415e8e commit fae5b21

File tree

5 files changed

+316
-3
lines changed

5 files changed

+316
-3
lines changed

ios/OAuthManager.xcodeproj/project.pbxproj

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
B76E094E1E768CE100A9AF9A /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = B76E094B1E768CE100A9AF9A /* README.md */; };
11+
B76E094F1E768CE100A9AF9A /* XMLReader.h in Headers */ = {isa = PBXBuildFile; fileRef = B76E094C1E768CE100A9AF9A /* XMLReader.h */; };
12+
B76E09501E768CE100A9AF9A /* XMLReader.m in Sources */ = {isa = PBXBuildFile; fileRef = B76E094D1E768CE100A9AF9A /* XMLReader.m */; };
1013
D935004D1D513CF700C7BA47 /* OAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D935004C1D513CF700C7BA47 /* OAuthManager.m */; };
1114
D9F2EAD31DA9A9650000BD52 /* OAuthClient.h in Headers */ = {isa = PBXBuildFile; fileRef = D9F2EAD11DA9A9650000BD52 /* OAuthClient.h */; };
1215
D9F2EAD41DA9A9650000BD52 /* OAuthClient.m in Sources */ = {isa = PBXBuildFile; fileRef = D9F2EAD21DA9A9650000BD52 /* OAuthClient.m */; };
@@ -65,6 +68,9 @@
6568
/* End PBXCopyFilesBuildPhase section */
6669

6770
/* Begin PBXFileReference section */
71+
B76E094B1E768CE100A9AF9A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
72+
B76E094C1E768CE100A9AF9A /* XMLReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLReader.h; sourceTree = "<group>"; };
73+
B76E094D1E768CE100A9AF9A /* XMLReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMLReader.m; sourceTree = "<group>"; };
6874
D91353961DA7849100AABC96 /* libOAuthManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libOAuthManager.a; sourceTree = BUILT_PRODUCTS_DIR; };
6975
D935004C1D513CF700C7BA47 /* OAuthManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OAuthManager.m; path = OAuthManager/OAuthManager.m; sourceTree = "<group>"; };
7076
D991AB771D1B237400DE9E58 /* Pods.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Pods.xcodeproj; path = Pods/Pods.xcodeproj; sourceTree = "<group>"; };
@@ -91,9 +97,21 @@
9197
/* End PBXFrameworksBuildPhase section */
9298

9399
/* Begin PBXGroup section */
100+
B76E094A1E768CE100A9AF9A /* XMLReader */ = {
101+
isa = PBXGroup;
102+
children = (
103+
B76E094B1E768CE100A9AF9A /* README.md */,
104+
B76E094C1E768CE100A9AF9A /* XMLReader.h */,
105+
B76E094D1E768CE100A9AF9A /* XMLReader.m */,
106+
);
107+
name = XMLReader;
108+
path = OAuthManager/XMLReader;
109+
sourceTree = SOURCE_ROOT;
110+
};
94111
D93EF9941DA77CBB00EC55A0 /* /Users/auser/Development/react-native/mine/OAuthManager/ios/OAuthManager.xcodeproj */ = {
95112
isa = PBXGroup;
96113
children = (
114+
B76E094A1E768CE100A9AF9A /* XMLReader */,
97115
D9F2EADF1DA9A9930000BD52 /* OAuthManager.h */,
98116
D9F2EAE01DA9A9930000BD52 /* OAuthManager.m */,
99117
D9F2EAE11DA9A9930000BD52 /* OAuthManagerConstants.h */,
@@ -143,6 +161,7 @@
143161
files = (
144162
D9F2EAD91DA9A9730000BD52 /* OAuth1Client.h in Headers */,
145163
D9F2EADE1DA9A9840000BD52 /* OAuthClientProtocol.h in Headers */,
164+
B76E094F1E768CE100A9AF9A /* XMLReader.h in Headers */,
146165
D9F2EADB1DA9A9730000BD52 /* OAuth2Client.h in Headers */,
147166
D9F2EAD31DA9A9650000BD52 /* OAuthClient.h in Headers */,
148167
D9F2EAE41DA9A9930000BD52 /* OAuthManagerConstants.h in Headers */,
@@ -232,11 +251,13 @@
232251
isa = PBXSourcesBuildPhase;
233252
buildActionMask = 2147483647;
234253
files = (
254+
B76E094E1E768CE100A9AF9A /* README.md in Sources */,
235255
D9F2EADC1DA9A9730000BD52 /* OAuth2Client.m in Sources */,
236256
D935004D1D513CF700C7BA47 /* OAuthManager.m in Sources */,
237257
D9F2EAD41DA9A9650000BD52 /* OAuthClient.m in Sources */,
238258
D9F2EADA1DA9A9730000BD52 /* OAuth1Client.m in Sources */,
239259
D9F2EAE31DA9A9930000BD52 /* OAuthManager.m in Sources */,
260+
B76E09501E768CE100A9AF9A /* XMLReader.m in Sources */,
240261
);
241262
runOnlyForDeploymentPostprocessing = 0;
242263
};

ios/OAuthManager/OAuthManager.m

+17-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#import "OAuthClient.h"
1616
#import "OAuth1Client.h"
1717
#import "OAuth2Client.h"
18+
#import "XMLReader.h"
1819

1920
@interface OAuthManager()
2021
@property (nonatomic) NSArray *pendingClients;
@@ -466,9 +467,22 @@ - (NSDictionary *) getConfigForProvider:(NSString *)name
466467
NSData *rawData = response.data;
467468

468469
NSError *err;
469-
NSArray *data = [NSJSONSerialization JSONObjectWithData:rawData
470-
options:kNilOptions
471-
error:&err];
470+
NSArray *data;
471+
472+
// Check if returned data is a valid JSON
473+
// != nil returned if the rawdata is not a valid JSON
474+
if ((data = [NSJSONSerialization JSONObjectWithData:rawData
475+
options:kNilOptions
476+
error:&err]) == nil) {
477+
// Resetting err variable.
478+
err = nil;
479+
480+
// Parse XML
481+
data = [XMLReader dictionaryForXMLData:rawData
482+
options:XMLReaderOptionsProcessNamespaces
483+
error:&err];
484+
}
485+
472486
if (err != nil) {
473487
NSDictionary *errResp = @{
474488
@"status": @"error",

ios/OAuthManager/XMLReader/README.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# XMLReader
2+
3+
This project comes from a component developed by Troy Brant and published on his website : http://troybrant.net/blog/2010/09/simple-xml-to-nsdictionary-converter/
4+
5+
I'm open sourcing some of the updates I've made on it.
6+
7+
8+
## Usage
9+
10+
NSData *data = ...; // some data that can be received from remote service
11+
NSError *error = nil;
12+
NSDictionary *dict = [XMLReader dictionaryForXMLData:data
13+
options:XMLReaderOptionsProcessNamespaces
14+
error:&error];
15+
16+
17+
## Requirements
18+
19+
Xcode 4.4 and above because project use the "auto-synthesized property" feature.
20+
21+
22+
## FAQ
23+
24+
#### Sometimes I get an `NSDictionary` while I must get an `NSArray`, why ?
25+
26+
In the algorithm of the `XMLReader`, when the parser found a new tag it automatically creates an `NSDictionary`, if it found another occurrence of the same tag at the same level in the XML tree it creates another dictionary and put both dictionaries inside an `NSArray`.
27+
28+
The consequence is: if you have a list that contains only one item, you will get an `NSDictionary` as result and not an `NSArray`.
29+
The only workaround is to check the class of the object contained for in the dictionary using `isKindOfClass:`. See sample code below :
30+
31+
NSData *data = ...;
32+
NSError *error = nil;
33+
NSDictionary *dict = [XMLReader dictionaryForXMLData:data error:&error];
34+
35+
NSArray *list = [dict objectForKey:@"list"];
36+
if (![list isKindOfClass:[NSArray class]])
37+
{
38+
// if 'list' isn't an array, we create a new array containing our object
39+
list = [NSArray arrayWithObject:list];
40+
}
41+
42+
// we can loop through items safely now
43+
for (NSDictionary *item in list)
44+
{
45+
// ...
46+
}
47+
48+
49+
#### I don't have enable ARC on my project, how can I use your library ?
50+
51+
You have 2 options:
52+
53+
* Use the branch "[no-objc-arc](https://github.com/amarcadet/XMLReader/tree/no-objc-arc)" that use manual reference counting.
54+
* **Better choice:** add the "-fobjc-arc" compiler flag on `XMLReader.m` file in your build phases.
55+
56+
#### I have trust issues, I don't want ARC, I prefer MRC, what can I do ?
57+
58+
Well, nobody is perfect but, still, you can use the branch "[no-objc-arc](https://github.com/amarcadet/XMLReader/tree/no-objc-arc)".
59+
60+
61+
## Contributions
62+
63+
Thanks to the original author of this component Troy Brant and to [Divan "snip3r8" Visagie](https://github.com/snip3r8) for providing ARC support.
64+
65+
66+
## License
67+
68+
Copyright (C) 2012 Antoine Marcadet
69+
70+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
71+
72+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
73+
74+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
75+
76+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/amarcadet/XMLReader/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
77+
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// XMLReader.h
3+
//
4+
// Created by Troy Brant on 9/18/10.
5+
// Updated by Antoine Marcadet on 9/23/11.
6+
// Updated by Divan Visagie on 2012-08-26
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
enum {
12+
XMLReaderOptionsProcessNamespaces = 1 << 0, // Specifies whether the receiver reports the namespace and the qualified name of an element.
13+
XMLReaderOptionsReportNamespacePrefixes = 1 << 1, // Specifies whether the receiver reports the scope of namespace declarations.
14+
XMLReaderOptionsResolveExternalEntities = 1 << 2, // Specifies whether the receiver reports declarations of external entities.
15+
};
16+
typedef NSUInteger XMLReaderOptions;
17+
18+
@interface XMLReader : NSObject <NSXMLParserDelegate>
19+
20+
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
21+
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
22+
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)errorPointer;
23+
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)errorPointer;
24+
25+
@end
+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//
2+
// XMLReader.m
3+
//
4+
// Created by Troy Brant on 9/18/10.
5+
// Updated by Antoine Marcadet on 9/23/11.
6+
// Updated by Divan Visagie on 2012-08-26
7+
//
8+
9+
#import "XMLReader.h"
10+
11+
#if !defined(__has_feature) || !__has_feature(objc_arc)
12+
#error "XMLReader requires ARC support."
13+
#endif
14+
15+
NSString *const kXMLReaderTextNodeKey = @"text";
16+
NSString *const kXMLReaderAttributePrefix = @"@";
17+
18+
@interface XMLReader ()
19+
20+
@property (nonatomic, strong) NSMutableArray *dictionaryStack;
21+
@property (nonatomic, strong) NSMutableString *textInProgress;
22+
@property (nonatomic, strong) NSError *errorPointer;
23+
24+
@end
25+
26+
27+
@implementation XMLReader
28+
29+
#pragma mark - Public methods
30+
31+
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
32+
{
33+
XMLReader *reader = [[XMLReader alloc] initWithError:error];
34+
NSDictionary *rootDictionary = [reader objectWithData:data options:0];
35+
return rootDictionary;
36+
}
37+
38+
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
39+
{
40+
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
41+
return [XMLReader dictionaryForXMLData:data error:error];
42+
}
43+
44+
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
45+
{
46+
XMLReader *reader = [[XMLReader alloc] initWithError:error];
47+
NSDictionary *rootDictionary = [reader objectWithData:data options:options];
48+
return rootDictionary;
49+
}
50+
51+
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
52+
{
53+
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
54+
return [XMLReader dictionaryForXMLData:data options:options error:error];
55+
}
56+
57+
58+
#pragma mark - Parsing
59+
60+
- (id)initWithError:(NSError **)error
61+
{
62+
self = [super init];
63+
if (self)
64+
{
65+
self.errorPointer = *error;
66+
}
67+
return self;
68+
}
69+
70+
- (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
71+
{
72+
// Clear out any old data
73+
self.dictionaryStack = [[NSMutableArray alloc] init];
74+
self.textInProgress = [[NSMutableString alloc] init];
75+
76+
// Initialize the stack with a fresh dictionary
77+
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
78+
79+
// Parse the XML
80+
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
81+
82+
[parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
83+
[parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
84+
[parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
85+
86+
parser.delegate = self;
87+
BOOL success = [parser parse];
88+
89+
// Return the stack's root dictionary on success
90+
if (success)
91+
{
92+
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
93+
return resultDict;
94+
}
95+
96+
return nil;
97+
}
98+
99+
100+
#pragma mark - NSXMLParserDelegate methods
101+
102+
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
103+
{
104+
// Get the dictionary for the current level in the stack
105+
NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];
106+
107+
// Create the child dictionary for the new element, and initilaize it with the attributes
108+
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
109+
[childDict addEntriesFromDictionary:attributeDict];
110+
111+
// If there's already an item for this key, it means we need to create an array
112+
id existingValue = [parentDict objectForKey:elementName];
113+
if (existingValue)
114+
{
115+
NSMutableArray *array = nil;
116+
if ([existingValue isKindOfClass:[NSMutableArray class]])
117+
{
118+
// The array exists, so use it
119+
array = (NSMutableArray *) existingValue;
120+
}
121+
else
122+
{
123+
// Create an array if it doesn't exist
124+
array = [NSMutableArray array];
125+
[array addObject:existingValue];
126+
127+
// Replace the child dictionary with an array of children dictionaries
128+
[parentDict setObject:array forKey:elementName];
129+
}
130+
131+
// Add the new child dictionary to the array
132+
[array addObject:childDict];
133+
}
134+
else
135+
{
136+
// No existing value, so update the dictionary
137+
[parentDict setObject:childDict forKey:elementName];
138+
}
139+
140+
// Update the stack
141+
[self.dictionaryStack addObject:childDict];
142+
}
143+
144+
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
145+
{
146+
// Update the parent dict with text info
147+
NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];
148+
149+
// Set the text property
150+
if ([self.textInProgress length] > 0)
151+
{
152+
// trim after concatenating
153+
NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
154+
[dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];
155+
156+
// Reset the text
157+
self.textInProgress = [[NSMutableString alloc] init];
158+
}
159+
160+
// Pop the current dict
161+
[self.dictionaryStack removeLastObject];
162+
}
163+
164+
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
165+
{
166+
// Build the text value
167+
[self.textInProgress appendString:string];
168+
}
169+
170+
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
171+
{
172+
// Set the error pointer to the parser's error object
173+
self.errorPointer = parseError;
174+
}
175+
176+
@end

0 commit comments

Comments
 (0)