Reputation: 9
<content name="Bikes" last_updated_time="">
<bike name="MGP30" image_url="Mobile_App/images/mgp30_bike_banner.png">
<tspecs>
<image url="images/our-bikes/mgp30.png"/>
<image url="images/our-bikes/mgp30_tech_spec/1.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/2.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/3.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/4.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/5.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/6.jpg"/>
<image url="images/our-bikes/mgp30_tech_spec/7.jpg"/>
<spec>
<name>Engine Type</name>
<value>4 strokes liquid cooled DOHC</value>
</spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
<spec></spec>
</tspecs>
<photos></photos>
<workshop></workshop>
</bike>
<bike name="GP125" image_url="Mobile_App/images/gp125_bike_banner.png"></bike>
</content>
This is the first time I am working with xml parsing using core data.I have the above xml data which i receive from the server. I am not bale to understand how to create the relationships between the entities. How do I parse it and keep storing it using core data.
Content is the root object with Bikes as child element. Each Bike element has a tspecs, photos and workshop data. Each tspecs has set of images, and set of specs data. Each spec data has a name and value.
Upvotes: 0
Views: 1582
Reputation: 17673
simply use NSXMLParser
class for parsing xml files. use the delegate methods of NSXMLParser class. delegate can recognize your elements, start of element, end of element, it also recognize start of document, end of document.
by checking your element name for matching you can save your string(i.e. data) into arrays & use that arrays to add in core data
below delegate method can use for detecting statrt of element
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
below delegate method for detecting end of element
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
always add values to array in didEndElement.
you can check your current element for matching like this
if ([elementName isEqualToString:@"firstElement"])
Upvotes: 0
Reputation: 10201
Parsing XML is a painstaking process in iOS. Using GDataXML to parse xml to a corresponding dictionary in array format is as follows.
#define kBikeName @"name"
#define kBikeImageURL @"image_url"
#define kBikeImages @"image"
#define kURL @"url"
#define kBikeSpecs @"spec"
#define kBikeSpecName @"name"
#define kBikeSpecValue @"value"
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"Bikes" ofType:@"xml"];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
GDataXMLDocument *document = [[GDataXMLDocument alloc]initWithData:fileData
encoding:NSUTF8StringEncoding
error:nil];
NSArray *tempBikes = [document nodesForXPath:@"//content/bike" error:nil];
NSMutableArray *bikes = [NSMutableArray array];
for (GDataXMLElement *element in tempBikes) {
NSMutableDictionary *bike = [NSMutableDictionary dictionary];
NSString *bikeName = [[element attributeForName:kBikeName] stringValue];
bike[kBikeName] = bikeName;
NSString *imageURL = [[element attributeForName:kBikeImageURL] stringValue];
bike[kBikeImageURL] = imageURL;
NSArray *tempImages = [element nodesForXPath:@"tspecs/image" error:nil];//elementsForName:@"image"];
NSMutableArray *images = [NSMutableArray array];
for (GDataXMLElement *imageElement in tempImages) {
NSString *url = [[imageElement attributeForName:kURL]stringValue];
if (url) {
[images addObject:url];
}
}
if (images) {
bike[@"Images"] = images;
}
NSArray *tempSpecs = [element nodesForXPath:@"tspecs/spec" error:nil];//elementsForName:@"spec"];
NSMutableArray *specs = [NSMutableArray array];
for (GDataXMLElement *specElement in tempSpecs) {
NSMutableDictionary *specDict = [NSMutableDictionary dictionary];
NSString *specName = [[specElement elementsForName:kBikeSpecName][0]stringValue];
if (specName) {
specDict[kBikeSpecName] = specName;
}
NSString *specValue = [[specElement elementsForName:kBikeSpecValue][0]stringValue];
if (specValue) {
specDict[kBikeSpecValue] = specValue;
}
if ([[specDict allKeys] count]) {
[specs addObject:specDict];
}
}
if (specs) {
bike[@"Specs"] = specs;
}
[bikes addObject:bike];
}
NSLog(@"%@",bikes);
Now you have array of dictionary objects. You could have formed NSManagedObject
s rite away. But incase if you want to go with JSON in future this would give more reuseable code.
You should have atleast three entities atleast. If you want you can also create Bike TechnicalSpec which would have one to one relationship with bike and one to many with image and spec. But that seems unnecessary.
Third party library MagicalRecord
provides codeless data importing. But it requires you to have uniqueID's to your entities.
For eg: If there is an entity Bike
, it should have a bikeID
. BikeImage
should have bikeImageID
and BikeSpec
should have bikeSpecID
. Since you don't have these it cannot be used. But I'm saying this as if you decide to change the structure these can be included to make use of this awesome feature.
Set the proper relationship and inverse relationship between the entities. Enumerate through the parsed array. Do the following steps
This don't restrict creation of duplicate entries.
Upvotes: 1
Reputation: 5173
Here is a template I commonly use. No 3rd party frameworks required. Most of this is driven from Apple's Event-Driven Programming Guide. I'll apologize for any code typos as I ripped this out of an app and tried to make it somewhat generic for you. The link is really the meat of the substance, though.
@implementation XMLParser
- (XMLParser *) initXMLParser {
appDelegate = (PWMSledUpdateAppDelegate *) [[UIApplication sharedApplication]delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"firstElement"])
{
//You now have your First Element name. I typically use an NSMutableDictionary to house the info
//In your example XML above you'd want to look for "content"
}
else if ([elementName isEqualToString:@"secondElement"])
{
//Second element name. Do something with it.
//In your example XML above you'd want to look for "bike"
}
//else if, etc, etc
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//"string" is your current element's value.
//using your example the value for content.bike.tspecs.spec.name = "Engine Type"
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"firstElement"])
{
NSLog(@"Read key \"%@\" with value \"%@\"",elementName, currentElementValue);
//you've now reached the end of your first element.
return;
}
else if ([elementName isEqualToString:@"secondElement"])
{
//you've now reached the end of your second element.
return;
}
//else if etc, etc
{
NSLog(@"Read key \"%@\" with value \"%@\"",elementName, currentElementValue);
[currUpdate setValue:currentElementValue forKey:elementName];
currentElementValue = nil;
}
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ( [elementName isEqualToString:@"firstElement"])
{
if (!myAttributes)
{
NSMutableArray *myAttributes = [[NSMutableArray alloc] init];
}
NSString *myName = [attributeDict objectForKey:@"name"];
NSString *myLastUpdate = [attributeDict objectForKey:@"last_updated_time"];
}
//else it etc, etc
}
Regarding housing the XML data I typically run with an NSMutableDictionary combined with a custom class with an intuitive naming convention so I can call it like myClass.name, myClass.lastUpdated, [myClass.name objectForKey @"MGP30"]..
Upvotes: 0