Array is remain still empty while fetching coordinates - iphone

I have array of country names and i want to get coordinates of them and store into another array. after this function my longitudes and latitudes arrays are remains still empty. do you have idea what is problem here ? i can't understand.
My Code is :
- (void)geocode {
NSString *strcountry=[[NSString alloc]init];
countryLati=[[NSMutableArray alloc]init];
countryLongi=[[NSMutableArray alloc]init];
countryName =[NSMutableArray arrayWithObjects:#"Bahrain",#"Bangladesh",#"Bhutan",#"China",#"Georgia",#"Hong Kong",nil ];
int count=[countryName count];
for(int i=0;i<count;i++)
{
strcountry=[countryName objectAtIndex:i];
[SVGeocoder geocode:strcountry
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error) {
//NSLog(#"placemarks = %#", placemarks);
CLPlacemark *place = [placemarks objectAtIndex:0];
CLLocation *location = place.location;
CLLocationCoordinate2D coord = location.coordinate;
NSString *tempLati=[[NSString alloc]initWithFormat:#"%g",coord.latitude];
NSString *tempLongi=[[NSString alloc]initWithFormat:#"%g",coord.longitude];
NSLog(#"-------------------------");
NSLog(#"Country : %#",strcountry);
NSLog(#"Latitude : %# ",tempLati);
NSLog(#"Longitude : %# ",tempLongi);
[countryLati addObject:tempLati];
[countryLongi addObject:tempLongi];
}];
}
NSLog(#"%d",[countryName count]);
NSLog(#"%d ",[countryLati count]);
NSLog(#"%d ",[countryLongi count]);
}

I accept Nuzhat Zari solution. Below is the code without using blocks and 3rd party Libraries. Its just a suggetion and showing you one more way of getting location details.
https://developers.google.com/maps/documentation/geocoding/
this URL has a good documentation for Google API.
- (void)geocode
{
NSString *strcountry=[[NSString alloc]init];
countryLati=[[NSMutableArray alloc]init];
countryLongi=[[NSMutableArray alloc]init];
countryName =[NSMutableArray arrayWithObjects:#"Bahrain",#"Bangladesh",#"Bhutan",#"China",#"Georgia",#"Hong Kong",nil ];
int count=[countryName count];
for(int i=0;i<count;i++)
{
strcountry=[countryName objectAtIndex:i];
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#&output=csv", strCountry];
NSString* webStringURL = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:webStringURL];
NSString *locationString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSLog(#"Location String = %#", locationString);
NSArray *receivedGoogleData = [[NSArray alloc] initWithArray:[locationString componentsSeparatedByString:#","]];
searchTermLocation = [[CLLocation alloc] initWithLatitude:[[receivedGoogleData objectAtIndex:2] doubleValue] longitude:[[receivedGoogleData objectAtIndex:3] doubleValue]];
[countryLati addObject:[[receivedGoogleData objectAtIndex:2] doubleValue]];
[countryLongi addObject:[[receivedGoogleData objectAtIndex:3] doubleValue]];
}
NSLog(#"%d",[countryName count]);
NSLog(#"%d ",[countryLati count]);
NSLog(#"%d ",[countryLongi count]);
}
#Mahesh Dhapa : Try this one, From my perspective, I feel this is easy than your way of SVGeoCoder, because no need to extra importing of SVGeocoder classes. BTW, SVGeocoder is simple. But, This is also straight forward without using 3rd party libraries.
You get response from Google like this
200,1,23.8954,34.5478
In which,
200 - Success
1 - Accuracy
23.8954 - Latitude of location
34.5478 - Longitude of location
You can also get this in JSON and XML Format with extra clear information, But you need to decode them to Dictionary from JSON and XML format to use them.
Example JSON Format :
{
"name": "Bahrain",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "Bahrain",
"AddressDetails": {
"Accuracy" : 1,
"Country" : {
"CountryName" : "Bahrain",
"CountryNameCode" : "BH"
}
},
"ExtendedData": {
"LatLonBox": {
"north": 26.3240565,
"south": 25.5798401,
"east": 50.8228639,
"west": 50.3781509
}
},
"Point": {
"coordinates": [ 50.5577000, 26.0667000, 0 ]
}
} ]
}
Example XML Response :
<kml xmlns="http://earth.google.com/kml/2.0">
<Response>
<name>Bahrain</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>Bahrain</address>
<AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" Accuracy="1">
<Country>
<CountryNameCode>BH</CountryNameCode>
<CountryName>Bahrain</CountryName>
</Country>
</AddressDetails>
<ExtendedData>
<LatLonBox north="26.3240565" south="25.5798401" east="50.8228639" west="50.3781509"/>
</ExtendedData>
<Point>
<coordinates>50.5577000,26.0667000,0</coordinates>
</Point>
</Placemark>
</Response>
</kml>

I think when you want to change local variable in block you need to declare it as block, as follows:
NSArray *stringsArray = #[ #"string 1",
#"String 21",
#"string 12",
#"String 11",
#"Strîng 21",
#"Striñg 21",
#"String 02" ];
NSLocale *currentLocale = [NSLocale currentLocale];
__block NSUInteger orderedSameCount = 0;
NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
NSRange string1Range = NSMakeRange(0, [string1 length]);
NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
if (comparisonResult == NSOrderedSame) {
orderedSameCount++;
}
return comparisonResult;
}];
NSLog(#"diacriticInsensitiveSortArray: %#", diacriticInsensitiveSortArray);
NSLog(#"orderedSameCount: %d", orderedSameCount);
Edit:A statement from reference.
A powerful feature of blocks is that they can modify variables in the
same lexical scope. You signal that a block can modify a variable
using the __block storage type modifier.
So you should declare countryLati and countryLongi as block storage.

Related

MapKit - Make route line follow streets when map zoomed in

I wrote simple application which draws route between two locations on MapKit. I am using Google Map API. I used resources I found online and here's the code I am using to make request to Google:
_httpClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://maps.googleapis.com/"]];
[_httpClient registerHTTPOperationClass: [AFJSONRequestOperation class]];
[_httpClient setDefaultHeader:#"Accept" value:#"application/json"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setObject:[NSString stringWithFormat:#"%f,%f", coordinate.latitude, coordinate.longitude] forKey:#"origin"];
[parameters setObject:[NSString stringWithFormat:#"%f,%f", endCoordinate.latitude, endCoordinate.longitude] forKey:#"destination"];
[parameters setObject:#"true" forKey:#"sensor"];
NSMutableURLRequest *request = [_httpClient requestWithMethod:#"GET" path: #"maps/api/directions/json" parameters:parameters];
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
NSInteger statusCode = operation.response.statusCode;
if (statusCode == 200)
{
NSLog(#"Success: %#", operation.responseString);
}
else
{
NSLog(#"Status code = %d", statusCode);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", operation.responseString);
}
];
[_httpClient enqueueHTTPRequestOperation:operation];
This works flawlessly. When I run this and try to show route between LA and Chicago, here's how it looks like:
BUT. When I zoom map to street level, here's how route looks like:
Does anyone know how can I achieve that route I am drawing follows streets when map is zoomed? I'd like route to show exact path through the streets. I don't know if some additional parameter needs to be added to my request to Google.
Any help or advice would be great. Many thanks in advance!
[edit #1: Adding request URL and response from Google]
My request URL after creating operation object from code above looks like this:
http://maps.googleapis.com/maps/api/directions/json?sensor=true&destination=34%2E052360,-118%2E243560&origin=41%2E903630,-87%2E629790
Just paste that URL to your browser and you will see the JSON data Google sends as the response which I get in my code also.
[edit #2: Parsing answer from Google and building the path]
- (void)parseResponse:(NSData *)response
{
NSDictionary *dictResponse = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableContainers error:nil];
NSArray *routes = [dictResponse objectForKey:#"routes"];
NSDictionary *route = [routes lastObject];
if (route)
{
NSString *overviewPolyline = [[route objectForKey: #"overview_polyline"] objectForKey:#"points"];
_path = [self decodePolyLine:overviewPolyline];
}
}
- (NSMutableArray *)decodePolyLine:(NSString *)encodedStr
{
NSMutableString *encoded = [[NSMutableString alloc] initWithCapacity:[encodedStr length]];
[encoded appendString:encodedStr];
[encoded replaceOccurrencesOfString:#"\\\\" withString:#"\\"
options:NSLiteralSearch
range:NSMakeRange(0, [encoded length])];
NSInteger len = [encoded length];
NSInteger index = 0;
NSMutableArray *array = [[NSMutableArray alloc] init];
NSInteger lat=0;
NSInteger lng=0;
while (index < len)
{
NSInteger b;
NSInteger shift = 0;
NSInteger result = 0;
do
{
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do
{
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
lng += dlng;
NSNumber *latitude = [[NSNumber alloc] initWithFloat:lat * 1e-5];
NSNumber *longitude = [[NSNumber alloc] initWithFloat:lng * 1e-5];
CLLocation *location = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
[array addObject:location];
}
return array;
}
It looks like Google is not giving you all the points, or you are not looking at all the points. Actually, I'd expect polylines between placemarks, not only placemarks like you seem to have (with a straight line).
Check DirectionsStatus in the response to see if you are limited
Provide the json data that Google sends back.
I'm not so sure they use a radically different Mercator projection from the one used by Google.
I believe that the projection used by MapKit is different than that used by Google Maps.
MapKit uses Cylindrical Mercator, while Google uses a variant of the Mercator Projection.
Converting Between Coordinate Systems
Although you normally specify
points on the map using latitude and longitude values, there may be
times when you need to convert to and from other coordinate systems.
For example, you typically use map points when specifying the shape of
overlays.
Quoted from Apple:

Convert String into special - splitting an NSString

I have a string like: "mocktail, wine, beer"
How can I convert this into: "mocktail", "wine", "beer"?
the following gives you the desired result:
NSString *_inputString = #"\"mocktail, wine, beer\"";
NSLog(#"input string : %#", _inputString);
NSLog(#"output string : %#", [_inputString stringByReplacingOccurrencesOfString:#", " withString:#"\", \""]);
the result is:
input string : "mocktail, wine, beer"
output string : "mocktail", "wine", "beer"
You need to use:
NSArray * components = [myString componentsSeparatedByString: #", "];
NSString *string = #"mocktail, wine, beer";
//remove whitespaces
NSString *trimmedString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//get array of string
NSArray *array = [trimmedString componentsSeparatedByString:#","];
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (NSString *trimmedString in array) {
NSString *newString = [NSMutableString stringWithFormat:#"'%#'", trimmedString];
[newArray addObject:newString];
}
//merge new strings
NSString *finalString = [NSString stringWithFormat:#"%#", [newArray objectAtIndex:0]];
for (NSInteger i = 1; i < [newArray count]; i++) {
finalString = [NSString stringWithFormat:#"%#, %#", finalString, [newArray objectAtIndex:i]];
}
Without knowing spesifically about iOS or objective-c, I assume you could use a split function.
In almost any higher level programming language there is such a function.
Try:
Objective-C split
This gets you an array of Strings. You can then practically do with those what you want to do, e.g. surrounding them with single quotes and appending them back together. :D

CLPlacemark - State Abbreviations?

I was wondering if it was possible to get the state abbreviations from CLPlacemark?
In the CLPlacemark Reference from Apple it states:
administrativeArea
The state or province associated with the placemark. (read-only)
#property(nonatomic, readonly) NSString *administrativeArea
Discussion
If the placemark location is Apple’s headquarters, for example, the value for this property would be the string “CA” or “California”.
but whenever I use it, I only get the full state (i.e California) and not the abbreviation (i.e CA). Can anyone help me here?
For anyone else that needs a solution for this, I've created a category class for CLPlacemark that returns the short state string. All you need to do is call myPlacemark shortState
CLPlacemark+ShortState.h
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#interface CLPlacemark (ShortState)
- (NSString *)shortState;
#end
CLPlacemark+ShortState.m
#import "CLPlacemark+ShortState.h"
#interface CLPlacemark (ShortStatePrivate)
- (NSDictionary *)nameAbbreviations;
#end
#implementation CLPlacemark (ShortState)
- (NSString *)shortState {
NSString *state = [self.administrativeArea lowercaseString];
if (state.length==0)
return nil;
return [[self nameAbbreviations] objectForKey:state];
}
- (NSDictionary *)nameAbbreviations {
static NSDictionary *nameAbbreviations = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
nameAbbreviations = [NSDictionary dictionaryWithObjectsAndKeys:
#"AL",#"alabama",
#"AK",#"alaska",
#"AZ",#"arizona",
#"AR",#"arkansas",
#"CA",#"california",
#"CO",#"colorado",
#"CT",#"connecticut",
#"DE",#"delaware",
#"DC",#"district of columbia",
#"FL",#"florida",
#"GA",#"georgia",
#"HI",#"hawaii",
#"ID",#"idaho",
#"IL",#"illinois",
#"IN",#"indiana",
#"IA",#"iowa",
#"KS",#"kansas",
#"KY",#"kentucky",
#"LA",#"louisiana",
#"ME",#"maine",
#"MD",#"maryland",
#"MA",#"massachusetts",
#"MI",#"michigan",
#"MN",#"minnesota",
#"MS",#"mississippi",
#"MO",#"missouri",
#"MT",#"montana",
#"NE",#"nebraska",
#"NV",#"nevada",
#"NH",#"new hampshire",
#"NJ",#"new jersey",
#"NM",#"new mexico",
#"NY",#"new york",
#"NC",#"north carolina",
#"ND",#"north dakota",
#"OH",#"ohio",
#"OK",#"oklahoma",
#"OR",#"oregon",
#"PA",#"pennsylvania",
#"RI",#"rhode island",
#"SC",#"south carolina",
#"SD",#"south dakota",
#"TN",#"tennessee",
#"TX",#"texas",
#"UT",#"utah",
#"VT",#"vermont",
#"VA",#"virginia",
#"WA",#"washington",
#"WV",#"west virginia",
#"WI",#"wisconsin",
#"WY",#"wyoming",
nil];
});
return nameAbbreviations;
}
#end
I think you can't get the abbreviations of the states but you can make your own class for this..
List all the states(states are standards)
compare those states and return the abbreviation
Code..
Class StateAbbreviation
StateAbbreviation.h
#interface StateAbbreviation : NSString {
}
+ (NSString *)allStates:(int)index;
+ (NSString *)abbreviatedState:(NSString *)strState;
#end
StateAbbreviation.m
#implementation StateAbbreviation
+ (NSString *)allStates:(NSString *)strState {
// Remove all space on the string
strState = [strState stringByReplacingOccurrencesOfString:#" " withString:#""];
//Sample states
NSArray *states = [[NSArray alloc] initWithObjects:
#"ALABAMA",
#"ALASKA", //AK
#"AMERICANSAMOA", //AS
#"ARIZONA", //AZ
#"ARKANSAS", //AR
#"CALIFORNIA", //CA
nil];
NSUInteger n = [states indexOfObject:strState];
if (n > [states count] - 1) {
strAbbreviation = #"NOSTATE";
}
else {
strAbbreviation =[self abbreviatedState:n];
}
[states release];
return strAbbreviation;
}
+ (NSString *)abbreviatedState:(int)index {
NSArray *states = [[NSArray alloc] initWithObjects:
#"AL",
#"AK",
#"AS",
#"AZ",
#"AR",
#"CA",
nil];
NSString *strAbbreviation = [states objectAtIndex:index];
[states release];
return strAbbreviation;
}
#end
When you call the class it should be something like this
NSString *upperCase = [#"California" uppercaseString]; // California could be from (NSString *)placemark.administrativeArea;
NSString *abbr = [StateAbbreviation allStates:upperCase];
NSLog(#"%#", abbr); // Result should be CA
This are only samples you can research all states something like this, states and their abbreviations also like this states and their abbreviations
I believe the documentation is just incorrect. The administrativeArea is always going to return the full state name for places in the United States. To get the state abbreviation you'll most likely have to create a dictionary look up table so that searching for the key "California" will return you the value "CA".
Here is another category using FormattedAddressLines, it returns a result like California, CA
-(NSString *) stateWithAbbreviation {
if ([[self.addressDictionary objectForKey:#"CountryCode"] isEqualToString:#"US"] && self.addressDictionary) {
NSDictionary *addressLines = [self.addressDictionary objectForKey:#"FormattedAddressLines"];
for (NSString* addressLine in addressLines) {
NSRange stateRange = [addressLine rangeOfString:self.postalCode options:NSCaseInsensitiveSearch];
if (stateRange.length > 0) {
NSRange lastSpace = [addressLine rangeOfString:#" " options:NSBackwardsSearch];
if (lastSpace.length > 0) {
NSString *state = [[addressLine substringToIndex:lastSpace.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
lastSpace = [state rangeOfString:#" " options:NSBackwardsSearch];
if (lastSpace.length > 0) {
NSString *abbr = [[state substringFromIndex:lastSpace.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [NSString stringWithFormat:#"%#, %#", self.administrativeArea, abbr];
}
}
}
}
}
return self.administrativeArea;
}
Not perfect but it works as long as Apple changes the format of the address lines I think.
For people who need the state list with objects and keys swapped (e.g. on iOS 7 I get "CA" from placemark.administrativeArea):
NSDictionary *nameAbbreviations = [NSDictionary dictionaryWithObjectsAndKeys:
#"alabama",#"AL",
#"alaska",#"AK",
#"arizona",#"AZ",
#"arkansas",#"AR",
#"california",#"CA",
#"colorado",#"CO",
#"connecticut",#"CT",
#"delaware",#"DE",
#"district of columbia",#"DC",
#"florida",#"FL",
#"georgia",#"GA",
#"hawaii",#"HI",
#"idaho",#"ID",
#"illinois",#"IL",
#"indiana",#"IN",
#"iowa",#"IA",
#"kansas",#"KS",
#"kentucky",#"KY",
#"louisiana",#"LA",
#"maine",#"ME",
#"maryland",#"MD",
#"massachusetts",#"MA",
#"michigan",#"MI",
#"minnesota",#"MN",
#"mississippi",#"MS",
#"missouri",#"MO",
#"montana",#"MT",
#"nebraska",#"NE",
#"nevada",#"NV",
#"new hampshire",#"NH",
#"new jersey",#"NJ",
#"new mexico",#"NM",
#"new york",#"NY",
#"north carolina",#"NC",
#"north dakota",#"ND",
#"ohio",#"OH",
#"oklahoma",#"OK",
#"oregon",#"OR",
#"pennsylvania",#"PA",
#"rhode island",#"RI",
#"south carolina",#"SC",
#"south dakota",#"SD",
#"tennessee",#"TN",
#"texas",#"TX",
#"utah",#"UT",
#"vermont",#"VT",
#"virginia",#"VA",
#"washington",#"WA",
#"west virginia",#"WV",
#"wisconsin",#"WI",
#"wyoming",#"WY",
nil];
As of at least iOS 8, CLPlacemark's administrativeArea returns a two-letter abbreviation for US States.
You don't need to extend CLPlacemark with a category like the one in the accepted answer as long as you're targeting iOS 8 and newer (which you should be by now).
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:#"1 Infinite Loop, Cupertino, CA" completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks firstObject];
NSLog(#"State: %#", placemark.administrativeArea);
}];
Run this and you'll get:
State: CA
SWIFT variant of dictionary
let states = [
"AL":"alabama",
"AK":"alaska",
"AZ":"arizona",
"AR":"arkansas",
"CA":"california",
"CO":"colorado",
"CT":"connecticut",
"DE":"delaware",
"DC":"district of columbia",
"FL":"florida",
"GA":"georgia",
"HI":"hawaii",
"ID":"idaho",
"IL":"illinois",
"IN":"indiana",
"IA":"iowa",
"KS":"kansas",
"KY":"kentucky",
"LA":"louisiana",
"ME":"maine",
"MD":"maryland",
"MA":"massachusetts",
"MI":"michigan",
"MN":"minnesota",
"MS":"mississippi",
"MO":"missouri",
"MT":"montana",
"NE":"nebraska",
"NV":"nevada",
"NH":"new hampshire",
"NJ":"new jersey",
"NM":"new mexico",
"NY":"new york",
"NC":"north carolina",
"ND":"north dakota",
"OH":"ohio",
"OK":"oklahoma",
"OR":"oregon",
"PA":"pennsylvania",
"RI":"rhode island",
"SC":"south carolina",
"SD":"south dakota",
"TN":"tennessee",
"TX":"texas",
"UT":"utah",
"VT":"vermont",
"VA":"virginia",
"WA":"washington",
"WV":"west virginia",
"WI":"wisconsin",
"WY":"wyoming"
]

How can I parse this xml using GDataXML parser?

<list>
<OrderData HASH="1408108039"></OrderData>
<OrderData HASH="208524692">
<id>97</id>
<customer>
<CustomerData HASH="2128670187"></CustomerData></customer>
<billingAddress></billingAddress><deliveryAddress></deliveryAddress>
<orderDetail>
<list>
<OrderDetailData HASH="516790072"></OrderDetailData>
<OrderDetailData HASH="11226247"></OrderDetailData>
<OrderDetailData HASH="11226247"></OrderDetailData>
</list>
</orderDetail>
<log/>
</OrderData>
<OrderData HASH="1502226778"></OrderData>
</list>
I cannot find a solution to find the number of OrderDetailData elements? I also read http://iphonebyradix.blogspot.com/2011/03/using-gdata-to-parse-xml-file.html this url.
Thanks in advance.
Edit:
I am explaining my requirement again. In this xml there will be multiple OrderData element. Now I have to count the number of OrderDetailData elemnts from a particular OrderData element. Suppose that, according to my xml, the current parsed xml has one OrderData element, named id and its value is 97. Now, I have to count how many OrderDetailData elements are contained in the OrderData(whichid` is 97).
This is a simple example how to retrieve some data. This example is very simple and not use XPath expression. I suggest you first understand how it works and then use XPath expression. In my opinion it is not useful to use XPath expression if you cannot understand how the parser works.
NSString* path = [[NSBundle mainBundle] pathForResource:#"test2" ofType:#"xml"];
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:path];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData
options:0 error:&error];
//NSLog(#"%#", doc.rootElement); // print the whole xml
NSArray *orderDataArray = [doc.rootElement elementsForName:#"OrderData"];
for (GDataXMLElement *orderDataElement in orderDataArray) {
if([orderDataElement childCount] > 0)
{
NSString *attributeForOrderDataElement = [(GDataXMLElement *) [orderDataElement attributeForName:#"HASH"] stringValue];
NSLog(#"attributeForOrderDataElement has value %#", attributeForOrderDataElement);
GDataXMLElement* idElement = (GDataXMLElement*)[[orderDataElement elementsForName:#"id"] objectAtIndex:0];
NSLog(#"id has value %#", idElement.stringValue);
GDataXMLElement* orderDetailElement = (GDataXMLElement*)[[orderDataElement elementsForName:#"orderDetail"] objectAtIndex:0];
GDataXMLElement* listElement = (GDataXMLElement*)[[orderDetailElement elementsForName:#"list"] objectAtIndex:0];
NSArray* orderDetailDataArray = [listElement elementsForName:#"OrderDetailData"];
int count = 0;
for (GDataXMLElement *orderDetailDataElement in orderDetailDataArray) {
NSString *attributeForOrderDetailDataElement = [(GDataXMLElement *) [orderDetailDataElement attributeForName:#"HASH"] stringValue];
NSLog(#"attributeForOrderDetailDataElement has value %#", attributeForOrderDetailDataElement);
count++;
}
NSLog(#"%d", count);
}
}
[doc release];
[xmlData release];
This is the output console:
attributeForOrderDataElement has value 208524692 <-- HASH value
id has value 97 <-- id value
attributeForOrderDetailDataElement has value 516790072 <-- HASH value
attributeForOrderDetailDataElement has value 11226247
attributeForOrderDetailDataElement has value 11226247
3 <-- the count
Hope it helps.
Edit
test2.xml contains your file but you could pass it as a string. You can also pass as parameters as string like the following:
NSString* xmlString = #"<list>"
"<OrderData HASH=\"1408108039\"></OrderData>"
"<OrderData HASH=\"208524692\">"
"<id>97</id>"
"<customer>"
"<CustomerData HASH=\"2128670187\"></CustomerData>"
"</customer>"
"<billingAddress></billingAddress>"
"<deliveryAddress></deliveryAddress>"
"<orderDetail>"
"<list>"
"<OrderDetailData HASH=\"516790072\"></OrderDetailData>"
"<OrderDetailData HASH=\"11226247\"></OrderDetailData>"
"<OrderDetailData HASH=\"11226247\"></OrderDetailData>"
"</list>"
"</orderDetail>"
"<log/>"
"</OrderData>"
"<OrderData HASH=\"1502226778\"></OrderData>"
"</list>";
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithXMLString:xmlString options:0 error:&error];
I found TouchXML easy in parsing and you can directly access data needed from xml
First of all Download TouchXML and add libxml2.dylib framework to your project.
change buildsetting for "Header Search Path" and add "/usr/include/libxml2"
Import TouchXML.h to your file
//Access document
CXMLDocument *parserDoc = [[CXMLDocument alloc] initWithContentsOfURL:url options:0 error:&err];
//Access root element and access children in heirarchy
CXMLElement *root = [parserDoc rootElement];
NSArray *places = [[[root children] objectAtIndex:0] children];
Else
//Access node by node
NSString *location =[[[[[parserDoc nodesForXPath:#"/xml_api_reply/weather/forecast_information/city" error:nil] objectAtIndex:0] attributeForName:#"data"] stringValue] retain];
If the OrderDetailData is present in 2nd object of array all the time, u can use the below
NSArray* arr = [[XMLelement elementForName:#"list"] elementsForName:#"OrderData"];
NSXMLElement* listElement = [[[arr objectAtIndex:1] elementForName:#"orderDetail"] elementForName:#"list"];
NSArray* orderDetailArray* = [listElement elementsForName:#"OrderDetailData"];
In this u can get the OrderDetailData elements in the array, and u can parse the data in loop. to get HASH value use.
NSString* hash = [[orderDetailArray objectAtIndex:0] stringValueForAttribute:#"HASH"];
This is the below code, i have tried. I got the right output.. check out
NSString* xmlString = #"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<list>"
"<OrderData HASH=\"1408108039\"></OrderData>"
"<OrderData HASH=\"208524692\">"
"<id>97</id>"
"<customer>"
"<CustomerData HASH=\"2128670187\"></CustomerData></customer>"
"<billingAddress></billingAddress><deliveryAddress></deliveryAddress>"
"<orderDetail>"
"<list>"
"<OrderDetailData HASH=\"516790072\"></OrderDetailData>"
"<OrderDetailData HASH=\"11226247\"></OrderDetailData>"
"<OrderDetailData HASH=\"11226247\"></OrderDetailData>"
"</list>"
"</orderDetail>"
"<log/></OrderData>"
"<OrderData HASH=\"1502226778\"></OrderData>"
"</list>";
NSXMLDocument* doc = [[NSXMLDocument alloc] initWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSXMLElement* rootElement = [doc rootElement];
NSArray* arr = [rootElement elementsForName:#"OrderData"];
NSXMLElement* orderDataElement;
for (int i = 0; i < [arr count]; i++)
{
int Id = 0;
Id = [[arr objectAtIndex:i] integerValueForNode:#"id"];
if (Id != 0)
{
orderDataElement = [arr objectAtIndex:i];
break;
}
}
NSArray* orderDetailArray = [[[orderDataElement elementForName:#"orderDetail"]
elementForName:#"list"]
elementsForName:#"OrderDetailData"];
for (int j = 0; j < [orderDetailArray count]; j++)
{
NSLog(#"%#", [[orderDetailArray objectAtIndex:j] stringValueForAttribute:#"HASH"]);
}

Using JSON Framework on iPhone - HELP!

Currently I am using the following code to parse the JSON link sent. This is how I also send a GET call to the Google Reader API for an upcoming iPhone application of mine.
- (NSArray *)subscriptionList
{
if(!cookies && [cookies count] == 0) {
[self requestSession];
}
NSString * url = #"http://www.google.com/reader/api/0/subscription/list?output=json&client=scroll";
ASIHTTPRequest * request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:url]];
[request setRequestMethod:#"GET"];
[request setRequestCookies:cookies];
[request addRequestHeader:#"Authorization" value:[NSString stringWithFormat:#"GoogleLogin auth=%#", [self auth]]];
[request startSynchronous];
subfeeds = [NSMutableArray array];
// Create new SBJSON parser object
SBJSON *parser = [[SBJSON alloc] init];
if ([request responseStatusCode] == 200) {
NSData * sixty = [request responseData];
NSString * body = [[NSString alloc] initWithData:sixty encoding:NSUTF8StringEncoding];
if (body) {
NSArray *feeds = [parser objectWithString:body error:nil];
NSLog(#"Array Contents: %#", [feeds valueForKey:#"subscriptions"]);
NSLog(#"Array Count: %d", [feeds count]);
NSDictionary *results = [body JSONValue];
NSArray *ohhai = [results valueForKey:#"subscriptions"];
for (NSDictionary *title in ohhai) {
subTitles = [title objectForKey:#"title"];
NSLog(#"title is: %#",subTitles);
}
}
}
return subfeeds;
[subTitles release];
[parser release];
}
I can successfully parse the JSON using the above code, and it successfully outputs the titles into NSLog. In my RootViewController.m, I call the following to grab this -(NSArray *)subscriptionList.
-(void)viewDidAppear:animated {
GoogleReader * reader = [[GoogleReader alloc] init];
[reader setEmail:gUserString];
[reader setPassword:gPassString];
//feedItems is a NSArray where we store the subscriptionList NSArray
feedItems = [reader subscriptionList];
//NSString *feedTitle = [];
NSLog(#"%#", feedItems);
[reader release];
// the rest of the function
}
The code above successfully works with the credentials entered. As you can see there is also a commented NSString called feedTitle. This is where I want to pull the #"title" from the parsed JSON but I do not know how to call it.
Any help would be greatly appreciated!
This is what the JSON source looks like:
{"subscriptions":
[
{"id":"","title":"","categories":[],"sortid":"","firstitemmsec":""},
{"id":"","title":"","categories":[],"sortid":"","firstitemmsec":""},
{"id":"","title":"","categories":[],"sortid":"","firstitemmsec":""},
{"id":"","title":"","categories":[],"sortid":"","firstitemmsec":""},
{"id":"","title":"","categories":[],"sortid":"","firstitemmsec":""}
]
}
I'm interested in only the "title" node.
Well, it would help if you added the source JSON but it's quite easy to grasp how SBJSON parses incoming JSON.
Just an example:
{ "myOutDict" : { "key1": "val1" , "key2" : "val2"} }
This JSON String would be parsed so you can access it by using this code
NSDictionary* myOuterdict = [feeds valueForKey:#"myOutDict"]);
NSString* val1 = [myOuterdict valueForKey:#"key1"]);
NSString* val2 = [myOuterdict valueForKey:#"key2"]);
Edit: Checked my personal Google Reader feed:
The JSON looks like this
{
"subscriptions": [{
"id": "feed/http://adambosworth.net/feed/",
"title": "Adam Bosworth's Weblog",
"categories": [],
"sortid": "0B5B845E",
"firstitemmsec": "1243627042599"
},
{
"id": "feed/http://feeds.feedburner.com/zukunftia2",
"title": "Zukunftia",
"categories": [],
"sortid": "FCABF5D4",
"firstitemmsec": "1266748722471"
}]
}
So the corresponding Objective C Code would be:
NSArray* subscriptions= [feeds valueForKey:#"subscriptions"]);
foreach(NSDictionary* item in subscriptions) {
// Do stuff
// NSString* title = [item valueForKey:#"title"]
// NSString* id = [item valueForKey:#"id"]
}
I'm not sure I understand the question. Are you trying to get a title for the feed as a whole, or per-item? Because I can't see a title property for the subscriptions array in the source JSON.