Converting NSDictionary to XML

Solution 1:

The short answer is: No, there is no built-in ability to do this in the Cocoa libraries.

Because you're writing rather than parsing, and presumably dealing with a limited universe of possible tags, the code to output XML is actually not that complicated. It should just be a simple method in your Invoice object, something like:

- (NSString*) postStringInXMLFormat
{
    NSMutableString* returnValue = [[NSMutableString alloc] init];
    if([self email])
    {
        [returnValue appendString:@"<email>"];
        [returnValue appendString:[self email]];
        [returnValue appendString:@"</email>"];
    }
    if([self invoice_date])
    ...

and so on. At the end return

[NSString stringWithString:returnValue]

There are plenty of third-party projects out there that try to generalize this process; several of them are listed in this answer:

Xml serialization library for iPhone Apps

But if all you're looking to do is create a single, stable format that your server side will recognize, and you don't have a ridiculous number of entities to convert, it's probably less work to roll your own.

Solution 2:

Here is a recursive way to convert a dictionary to a string of XML. It handles dictionaries, arrays, and strings. Dictionary keys are the XML tags and the dictionary objects are used as values or child items in the tree. In the case of an array each element in the array is placed on the same child level with the same tag.

- (NSString*)ConvertDictionarytoXML:(NSDictionary*)dictionary  withStartElement:(NSString*)startElement{
    NSMutableString *xml = [[NSMutableString alloc] initWithString:@""];
    [xml appendString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
    [xml appendString:[NSString stringWithFormat:@"<%@>",startElement]];
    [self convertNode:dictionary withString:xml andTag:nil];
    [xml appendString:[NSString stringWithFormat:@"</%@>",startElement]];
    NSString *finalXML=[xml stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"];
    NSLog(@"%@",xml);
    return finalXML;
}

- (void)convertNode:(id)node withString:(NSMutableString *)xml andTag:(NSString *)tag{
    if ([node isKindOfClass:[NSDictionary class]] && !tag) {
    NSArray *keys = [node allKeys];
    for (NSString *key in keys) {
        [self convertNode:[node objectForKey:key] withString:xml andTag:key];
    }
    }else if ([node isKindOfClass:[NSArray class]]) {
        for (id value in node) {
             [self convertNode:value withString:xml andTag:tag];
        }
    }else {
        [xml appendString:[NSString stringWithFormat:@"<%@>", tag]];
        if ([node isKindOfClass:[NSString class]]) {
        [xml appendString:node];
        }else if ([node isKindOfClass:[NSDictionary class]]) {
            [self convertNode:node withString:xml andTag:nil];
        }
        [xml appendString:[NSString stringWithFormat:@"</%@>", tag]];
    }
}

Solution 3:

If you wouldn't use libraries or need additional customization:

- (NSString*)convertDictionaryToXML:(NSDictionary*)dictionary withStartElement:(NSString*)startElement
{
    return [self convertDictionaryToXML:dictionary withStartElement:startElement isFirstElement:YES];
}

- (NSString*)convertDictionaryToXML:(NSDictionary*)dictionary withStartElement:(NSString*)startElement isFirstElement:(BOOL) isFirstElement
{
    NSMutableString *xml = [[NSMutableString alloc] initWithString:@""];
    NSArray *arr = [dictionary allKeys];
    if (isFirstElement)
    {
        [xml appendString:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"];
    }
    [xml appendString:[NSString stringWithFormat:@"<%@>\n", startElement]];
    for(int i=0; i < [arr count]; i++)
    {
        NSString *nodeName = [arr objectAtIndex:i];
        id nodeValue = [dictionary objectForKey:nodeName];
        if([nodeValue isKindOfClass:[NSArray class]])
        {
            if([nodeValue count]>0)
            {
                for(int j=0;j<[nodeValue count];j++)
                {
                    id value = [nodeValue objectAtIndex:j];
                    if([value isKindOfClass:[NSDictionary class]])
                    {
                        [xml appendString:[self convertDictionaryToXML:value withStartElement:nodeName isFirstElement:NO]];
                    }
                }
            }
        }
        else if([nodeValue isKindOfClass:[NSDictionary class]])
        {
            [xml appendString:[self convertDictionaryToXML:nodeValue withStartElement:nodeName isFirstElement:NO]];
        }
        else
        {
            if([nodeValue length]>0){
                [xml appendString:[NSString stringWithFormat:@"<%@>",nodeName]];
                [xml appendString:[NSString stringWithFormat:@"%@",[dictionary objectForKey:nodeName]]];
                [xml appendString:[NSString stringWithFormat:@"</%@>\n",nodeName]];
            }
        }
    }

    [xml appendString:[NSString stringWithFormat:@"</%@>\n",startElement]];

    NSString *finalxml=[xml stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"];

    return finalxml;
}

and call this as:

NSString *xmlString = [self convertDictionaryToXML:data withStartElement:startElementName];