2

Hey All! I am trying to use wonderxml (http://code.google.com/p/wonderxml/) to convert my xml into objective-c objects. It works for a simple case like below... XML:

<Defn>
   <name>Test1</name>
   <add>my address</add>
</Defn>

Corresponding Class interface:

#import <Foundation/Foundation.h>
@interface Defn: NSObject {
    NSString *name;
    NSString *add;
 }
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *add;

@end

However if i have a XML structure like below, what should i do?

   <Defn>
      <contact>
        <name>Test1</name>
        <add>my address</add>
      </contact>
      <contact>
        <name>Test1</name>
        <add>my address</add>
      </contact>
      .
      .
   </Defn>

What should be my class interface?

hrishib
  • 23
  • 3

4 Answers4

1

In your first example the XML file goes from has the key Defn holding to strings and you ObjC example follows this pattern with an object called Defn, which contains 2 strings.

For your second example, where you have contact holding the strings and then multiple contact's in Defn, you will probably want to use a similar pattern. Except with your Defn class being renamed to contacts and then the new Defn class holding an array of the contact objects. So my implementation would be.

contact.h

#import <Foundation/Foundation.h>
@interface contact: NSObject {
    NSString *name;
    NSString *add;
 }
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *add;
@end

Defn.h

#import <Foundation/Foundation.h>
@interface Defn: NSObject {
    NSMutableArray *contacts;
 }
@property (assign) NSMutableArray * contacts;
@end

This might not be the best solution but it follows the XML structure quite well and is a logical step from your original Class interface example.

Kyle Howells
  • 3,008
  • 1
  • 25
  • 35
  • Sorry for a later reply as i had to move away from this one. Back to it now. How does the interface Defn know that NSMutableArray *contacts contains objects of type contact? – hrishib Mar 29 '11 at 05:05
  • @hrishib - Well it doesn't know that but as you are writing the class you can make sure that you only put objects of type contact into it. If though you mean, "how will you access the properties name and add from the array when it returns an NSObject?" you can cast the object you get from the array as being of type contact. `contact *firstContact = (contact*)[contacts objectAtIndex:1];` If that isn't what you meant could you rephrase the question. – Kyle Howells Apr 01 '11 at 12:15
0

I've made an update to the XmlParser parser class so that you can pass in an array of objects and the xml you are looking for. Make the following additions to the indicated files and you will be able to accomplish what you are looking for:

XmlParser.h

-(NSString *)arrayToXml:(NSArray *)objects andTag:(NSString *)tag inNameSpace:(NSString *)nameSpace;

XmlParser.m

- (void)addArray:(NSArray *)objects toParentElement:(GDataXMLElement *)parent
{
    for (id object in objects) {

        const char *objectName = class_getName([object class]);

        NSString *objectNameStr = [[NSString alloc] initWithBytes:objectName length:strlen(objectName) encoding:NSASCIIStringEncoding];

        GDataXMLElement * objectElement = [GDataXMLNode elementWithName:objectNameStr];

        NSMutableDictionary * propertyDic = [XmlParser propertDictionary:object];

        NSString *nodeValue = [NSString stringWithFormat:@"%@",@""];

        for (NSString *key in propertyDic) {

            if ([object valueForKey:key] != nil) {

                if ([[object valueForKey:key] isKindOfClass:[NSSet class]]) {

                    GDataXMLElement * parentElement = [GDataXMLNode elementWithName:key];
                    [self addSet:[object valueForKey:key] toParentElement:parentElement];
                    [objectElement addChild:parentElement];

                } else {

                    if ([[propertyDic objectForKey:key] isEqualToString:@"l"]) {

                        nodeValue = [NSString stringWithFormat:@"%llu",[[object valueForKey:key] unsignedLongLongValue]];

                    } else if ([[propertyDic objectForKey:key] isEqualToString:@"i"]) {

                        nodeValue = [NSString stringWithFormat:@"%d",[object valueForKey:key]];

                    } else {

                        nodeValue = [NSString stringWithFormat:@"%@",[object valueForKey:key]];
                    }

                    GDataXMLElement * childElement = [GDataXMLNode elementWithName:key stringValue:nodeValue];
                    [objectElement addChild:childElement];
                }
            }
        }

        [parent addChild:objectElement];

        [objectNameStr release];
    }
}

//convert object to xml string, you could modify the type maping to fit your requirement, 
//the reperesentation of type could be found in objective-c runtime reference.
-(NSString *)arrayToXml:(NSArray *)objects andTag:(NSString *)tag inNameSpace:(NSString *)nameSpace
{
    GDataXMLElement *parent = [GDataXMLNode elementWithName:tag];
    [parent addNamespace:[GDataXMLNode namespaceWithName:nil stringValue:nameSpace]];

    [self addSet:objects toParentElement:parent];

    GDataXMLDocument *document = [[GDataXMLDocument alloc] initWithRootElement:parent];

    NSData *xmlData = document.XMLData;

    NSString *xmlString = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];

    NSString *bodyString = [xmlString substringFromIndex:21];

    NSString *headerString = [XmlHeaderHelper generateXmlHeader:tag and:nameSpace];

    xmlString = [NSString stringWithFormat:@"%@%@", headerString, bodyString];

    [document release];

    return xmlString;
}

So you will make the following call passing in an array of objects:

NSArray *contacts = [NSArray arrayFromObjects: contact1, contact2, nil];
[parser arrayToXml:contacts andTag:@"Defn" inNameSpace:@"http://mycontacts.com"]
saulpower
  • 1,258
  • 1
  • 10
  • 13
0

Depending on the complexity of models (in your case - too basic might as well just use GData), your mileage for my solution is gonna vary.

But you could mix WonderXML with its ancestor GDataXMLDocument. (haven't tested the code below)

// NSMutableArray *contacts = [[NSMutableArray alloc]initWithCapacity:0];

- (void) parseXMLString:(NSString *)xmlString {
    XmlParser *parser = [[[XmlParser alloc] init] autorelease];
    NSData *xmlData = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *xmlError;

    GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&xmlError];
    if (xmlDocument == nil) { 
        NSLog(@" Error parsing the XML Document");
        return; 
    }
    NSArray *contactElements = [[xmlDocument rootElement] elementsForName:@"Defn"];

    for (GDataXMLElement *contactElement in contactElements) {
        NSString  *contactString = [[[[contactElement attributes] objectAtIndex:0] stringValue];
        Contact *contact = [[[Contact alloc] init] autorelease];
        NSMutableArray *contactArray = [parser fromXml:contactString withObject:contact];
        // this should be one object in an array
        [contacts addObject:[contactArray objectAtIndex:0]];
    }
}
johndpope
  • 5,035
  • 2
  • 41
  • 43
-1

There are number of xml parsers, i am using xpathquery for iphone and i found it is easier to use, follow this http://cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html hope this helps.

pradeepa
  • 4,104
  • 5
  • 31
  • 41
  • Thanks pradeepa! But essentially they would work on similar principle. I just want to know what interface i might have to write for the second XML snippet. – hrishib Mar 15 '11 at 11:25
  • array of dictionary, where each array item will be a dictionary of contacts having name and address. – pradeepa Mar 15 '11 at 11:38
  • add name and address strings as key value pair to NSDictionary and add that to NSArray – pradeepa Mar 15 '11 at 11:43