Skip to content

Handling XML response data in IOS #88

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 2 commits into from
May 8, 2017
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
21 changes: 21 additions & 0 deletions ios/OAuthManager.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
B76E094E1E768CE100A9AF9A /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = B76E094B1E768CE100A9AF9A /* README.md */; };
B76E094F1E768CE100A9AF9A /* XMLReader.h in Headers */ = {isa = PBXBuildFile; fileRef = B76E094C1E768CE100A9AF9A /* XMLReader.h */; };
B76E09501E768CE100A9AF9A /* XMLReader.m in Sources */ = {isa = PBXBuildFile; fileRef = B76E094D1E768CE100A9AF9A /* XMLReader.m */; };
D935004D1D513CF700C7BA47 /* OAuthManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D935004C1D513CF700C7BA47 /* OAuthManager.m */; };
D9F2EAD31DA9A9650000BD52 /* OAuthClient.h in Headers */ = {isa = PBXBuildFile; fileRef = D9F2EAD11DA9A9650000BD52 /* OAuthClient.h */; };
D9F2EAD41DA9A9650000BD52 /* OAuthClient.m in Sources */ = {isa = PBXBuildFile; fileRef = D9F2EAD21DA9A9650000BD52 /* OAuthClient.m */; };
Expand Down Expand Up @@ -65,6 +68,9 @@
/* End PBXCopyFilesBuildPhase section */

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

/* Begin PBXGroup section */
B76E094A1E768CE100A9AF9A /* XMLReader */ = {
isa = PBXGroup;
children = (
B76E094B1E768CE100A9AF9A /* README.md */,
B76E094C1E768CE100A9AF9A /* XMLReader.h */,
B76E094D1E768CE100A9AF9A /* XMLReader.m */,
);
name = XMLReader;
path = OAuthManager/XMLReader;
sourceTree = SOURCE_ROOT;
};
D93EF9941DA77CBB00EC55A0 /* /Users/auser/Development/react-native/mine/OAuthManager/ios/OAuthManager.xcodeproj */ = {
isa = PBXGroup;
children = (
B76E094A1E768CE100A9AF9A /* XMLReader */,
D9F2EADF1DA9A9930000BD52 /* OAuthManager.h */,
D9F2EAE01DA9A9930000BD52 /* OAuthManager.m */,
D9F2EAE11DA9A9930000BD52 /* OAuthManagerConstants.h */,
Expand Down Expand Up @@ -143,6 +161,7 @@
files = (
D9F2EAD91DA9A9730000BD52 /* OAuth1Client.h in Headers */,
D9F2EADE1DA9A9840000BD52 /* OAuthClientProtocol.h in Headers */,
B76E094F1E768CE100A9AF9A /* XMLReader.h in Headers */,
D9F2EADB1DA9A9730000BD52 /* OAuth2Client.h in Headers */,
D9F2EAD31DA9A9650000BD52 /* OAuthClient.h in Headers */,
D9F2EAE41DA9A9930000BD52 /* OAuthManagerConstants.h in Headers */,
Expand Down Expand Up @@ -232,11 +251,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B76E094E1E768CE100A9AF9A /* README.md in Sources */,
D9F2EADC1DA9A9730000BD52 /* OAuth2Client.m in Sources */,
D935004D1D513CF700C7BA47 /* OAuthManager.m in Sources */,
D9F2EAD41DA9A9650000BD52 /* OAuthClient.m in Sources */,
D9F2EADA1DA9A9730000BD52 /* OAuth1Client.m in Sources */,
D9F2EAE31DA9A9930000BD52 /* OAuthManager.m in Sources */,
B76E09501E768CE100A9AF9A /* XMLReader.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
4 changes: 2 additions & 2 deletions ios/OAuthManager/OAuthManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

#if __has_include("RCTBridgeModule.h")
#import "RCTBridgeModule.h"
else
#else
#import <React/RCTBridgeModule.h>
#endif

#if __has_include("RCTLinkingManager.h")
#import "RCTLinkingManager.h"
else
#else
#import <React/RCTLinkingManager.h>
#endif

Expand Down
20 changes: 17 additions & 3 deletions ios/OAuthManager/OAuthManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "OAuthClient.h"
#import "OAuth1Client.h"
#import "OAuth2Client.h"
#import "XMLReader.h"

@interface OAuthManager()
@property (nonatomic) NSArray *pendingClients;
Expand Down Expand Up @@ -464,9 +465,22 @@ - (NSDictionary *) getConfigForProvider:(NSString *)name
NSData *rawData = response.data;

NSError *err;
NSArray *data = [NSJSONSerialization JSONObjectWithData:rawData
options:kNilOptions
error:&err];
NSArray *data;

// Check if returned data is a valid JSON
// != nil returned if the rawdata is not a valid JSON
if ((data = [NSJSONSerialization JSONObjectWithData:rawData
options:kNilOptions
error:&err]) == nil) {
// Resetting err variable.
err = nil;

// Parse XML
data = [XMLReader dictionaryForXMLData:rawData
options:XMLReaderOptionsProcessNamespaces
error:&err];
}

if (err != nil) {
NSDictionary *errResp = @{
@"status": @"error",
Expand Down
77 changes: 77 additions & 0 deletions ios/OAuthManager/XMLReader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# XMLReader

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/

I'm open sourcing some of the updates I've made on it.


## Usage

NSData *data = ...; // some data that can be received from remote service
NSError *error = nil;
NSDictionary *dict = [XMLReader dictionaryForXMLData:data
options:XMLReaderOptionsProcessNamespaces
error:&error];


## Requirements

Xcode 4.4 and above because project use the "auto-synthesized property" feature.


## FAQ

#### Sometimes I get an `NSDictionary` while I must get an `NSArray`, why ?

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`.

The consequence is: if you have a list that contains only one item, you will get an `NSDictionary` as result and not an `NSArray`.
The only workaround is to check the class of the object contained for in the dictionary using `isKindOfClass:`. See sample code below :

NSData *data = ...;
NSError *error = nil;
NSDictionary *dict = [XMLReader dictionaryForXMLData:data error:&error];

NSArray *list = [dict objectForKey:@"list"];
if (![list isKindOfClass:[NSArray class]])
{
// if 'list' isn't an array, we create a new array containing our object
list = [NSArray arrayWithObject:list];
}

// we can loop through items safely now
for (NSDictionary *item in list)
{
// ...
}


#### I don't have enable ARC on my project, how can I use your library ?

You have 2 options:

* Use the branch "[no-objc-arc](https://github.com/amarcadet/XMLReader/tree/no-objc-arc)" that use manual reference counting.
* **Better choice:** add the "-fobjc-arc" compiler flag on `XMLReader.m` file in your build phases.

#### I have trust issues, I don't want ARC, I prefer MRC, what can I do ?

Well, nobody is perfect but, still, you can use the branch "[no-objc-arc](https://github.com/amarcadet/XMLReader/tree/no-objc-arc)".


## Contributions

Thanks to the original author of this component Troy Brant and to [Divan "snip3r8" Visagie](https://github.com/snip3r8) for providing ARC support.


## License

Copyright (C) 2012 Antoine Marcadet

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:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

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.

[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/amarcadet/XMLReader/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

25 changes: 25 additions & 0 deletions ios/OAuthManager/XMLReader/XMLReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// XMLReader.h
//
// Created by Troy Brant on 9/18/10.
// Updated by Antoine Marcadet on 9/23/11.
// Updated by Divan Visagie on 2012-08-26
//

#import <Foundation/Foundation.h>

enum {
XMLReaderOptionsProcessNamespaces = 1 << 0, // Specifies whether the receiver reports the namespace and the qualified name of an element.
XMLReaderOptionsReportNamespacePrefixes = 1 << 1, // Specifies whether the receiver reports the scope of namespace declarations.
XMLReaderOptionsResolveExternalEntities = 1 << 2, // Specifies whether the receiver reports declarations of external entities.
};
typedef NSUInteger XMLReaderOptions;

@interface XMLReader : NSObject <NSXMLParserDelegate>

+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)errorPointer;

@end
176 changes: 176 additions & 0 deletions ios/OAuthManager/XMLReader/XMLReader.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//
// XMLReader.m
//
// Created by Troy Brant on 9/18/10.
// Updated by Antoine Marcadet on 9/23/11.
// Updated by Divan Visagie on 2012-08-26
//

#import "XMLReader.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "XMLReader requires ARC support."
#endif

NSString *const kXMLReaderTextNodeKey = @"text";
NSString *const kXMLReaderAttributePrefix = @"@";

@interface XMLReader ()

@property (nonatomic, strong) NSMutableArray *dictionaryStack;
@property (nonatomic, strong) NSMutableString *textInProgress;
@property (nonatomic, strong) NSError *errorPointer;

@end


@implementation XMLReader

#pragma mark - Public methods

+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:0];
return rootDictionary;
}

+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data error:error];
}

+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data options:options];
return rootDictionary;
}

+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data options:options error:error];
}


#pragma mark - Parsing

- (id)initWithError:(NSError **)error
{
self = [super init];
if (self)
{
self.errorPointer = *error;
}
return self;
}

- (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
{
// Clear out any old data
self.dictionaryStack = [[NSMutableArray alloc] init];
self.textInProgress = [[NSMutableString alloc] init];

// Initialize the stack with a fresh dictionary
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];

// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];

[parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
[parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
[parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];

parser.delegate = self;
BOOL success = [parser parse];

// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
return resultDict;
}

return nil;
}


#pragma mark - NSXMLParserDelegate methods

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];

// Create the child dictionary for the new element, and initilaize it with the attributes
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict addEntriesFromDictionary:attributeDict];

// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue)
{
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]])
{
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else
{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];

// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}

// Add the new child dictionary to the array
[array addObject:childDict];
}
else
{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}

// Update the stack
[self.dictionaryStack addObject:childDict];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];

// Set the text property
if ([self.textInProgress length] > 0)
{
// trim after concatenating
NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];

// Reset the text
self.textInProgress = [[NSMutableString alloc] init];
}

// Pop the current dict
[self.dictionaryStack removeLastObject];
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[self.textInProgress appendString:string];
}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
self.errorPointer = parseError;
}

@end
Loading