Regular problem with XML to JSON Converters

by: dermdaly

Here at tapadoo, we’ve done a number of apps which communicate with back end data servers. Typically this means a RESTful API with JSON or XML payloads.
There’s one issue we’ve come across on a number of projects, where we’re dealing with an existing Web API which is presenting the payloads in JSON.
The problem is where arrays are incorrectly serialized in certain cases. Specifically we’ve seen:

  • When an array has more than 1 entry, it is serialized correctly
  • When the array has only 1 entry, it is serialized not as an array, but as a single dictionary.

Firstly, lets explain why this has occurred
If you consider the following XML:

<team>
    <employee>
      <name>Joe</name>
      <surname>Bloggs</surname>
  </employee>
  <employee>
    <name>Jane</name>
    <surname>Doe</surname>
  </employee>
</team>

A typical XML to JSON converter will convert this as follows:

{
  team:{
    employee:[
      {
        name:'Joe',
        surname:'Bloggs'
      },
      {
        name:'Jane',
        surname:'Doe'
      }
    ]
  }
}

I.e It is converted to a Dictionary, with a key called ‘team’ whose value is a Dictionary, with a key called ’employee’ which is an array of Dictionaries (with keys of ‘name’ and ‘surname’).

Great.
However if the XML was a team with a single entry:

<team>
    <employee>
      <name>Joe</name>
      <surname>Bloggs</surname>
  </employee>
</team>

The XML to JSON converter has no context on Employee. It has now way of knowing that this is potentially a repeating group (especially if it has no XSD to consult), so it converts the XML to the following JSON:

{
  team:{
    employee:{
      name:'Joe',
      surname:'Bloggs'
    }
  }
}

So, this is now a Dictionary, with a key called ‘team’ whose value is a dictionary with a key called ’employee’ whose value is a Dictionary.

The problem here is when we go to parse, we expect the value of ’employee’ to be an array (which it isn’t), and this can lead to problems in the code.

And..due to Objective-C loose approach to type, the following code will run:

NSArray *array = [teamDict objectForKey:@"employee"];

In those cases where there team has a number of employees, the above code works fine, and the pointer, array stores an array. In those cases where a team as a single employee, the above code runs, however the pointer, array will actually point to a NSDictionary. So the following code (for example)

int numEntries = [array count];

Will sometimes work, and sometimes lead to a runtime error, which on iPhone will manifest itself as a crash.

So how do we fix this?
Well, if you have control over the server side, the simple question is to fix it in the first place. Note: This is a common problem with JAXB, a popular java XML binding mechanism often used in REST services written in Java. We’ve come across some examples on how to work around it here and here.

If you have no control over this, you may be able to handle this in your Objective-C code. Here’s some sample code we’ve started to use; It checks the data type at runtime, and if necessary, converts it to a single element array

+(NSArray *) getArrayFromJSONDictionary:(NSDictionary *)parent 
               forKey:(NSString *)key {
  id obj = [parent objectForKey:key];
  if([obj isKindOfClass:[NSArray class]]) {
    return obj;
  }
  if([obj isEqual:[NSNull null]]) {
    NSLog(@"Warning: object for key %@ is null", key);
    // Return an empty array
    return [[[NSArray alloc] init] autorelease];
  }
  NSLog(@"Warning object for key %@ is of type %@",
        key, [[obj class] description]);
  NSArray *ret = [NSArray arrayWithObject:obj];
  return ret;
}

You May Also Like

How apps are playing their part in COVID-19

How apps are playing their part in COVID-19

Technology is playing an increasingly important role during the COVID-19 pandemic. Not only is it playing its part in connecting us, it's providing us with platforms to work, learn and interact with one another as we navigate our new norm of working remotely. An now...

read more
Creating the Perfect App – Guaranteeing Success

Creating the Perfect App – Guaranteeing Success

So, your app idea has passed all tests and you have found a way to finance its development. Now its time to guarantee its success. Finding a developer Finding the right developer to build your app plays an important role in its success. Picking a development company...

read more

6 Comments

  1. Avatar

    While developing apps, just like these, I have also met this issue. Although, I’ve always seen it the other way around. JSON in this case has an avantage when used with objective-c, since the principals of storing is so similar (JSON-array->NSArray and JSON-object->NSDictionary). I’m having problems parsing XML code in objective-c for this very reason… When is the object an array and when is it a dictionary…
    Anyhow, I’m kinda explaining the confer of your post back at you here. The only thing I’m really saying is that you name this a problem with JSON, I’d say it’s a problem with XML. Then again, I’m not really a XML fan 😉

    Reply
  2. dermdaly

    Interestingly, more than one person on twitter has suggested that its a bug in the API, and therefore we should make the API owner fix their API.
    This is a blinkered answer. If you’re in the business of writing software for clients, you’ll know that you often have to work with whatever is presented to you. Of course you can highlight the API issue, and suggest it may be fixed, but remember you may be dealing with a legacy system, or something that has had a great deal of bedding in…and here you are writing new client side software where you’re in the position to work around API shortcomings.
    In short: If the client is willing to fix the API, great; If not we use techniques like the one above.

    Reply
  3. Avatar

    The json converter does have context to the xml, because the xml should be following a schema! In the xml schema, if ‘maxOccur=”unbounded”‘, then it should return an array, no matter how few entries. Not having or following the schema is why these converters are breaking! As you mentioned, it’s an impossible task because the json doesn’t have any context…but only because it’s ignoring the context (the xml schema).

    Reply
    • dermdaly

      Good point; thanks for commenting. What if the entry said “maxOccurs=1”? Could we end p with the same problem?

      Reply
    • Avatar

      Though we are using maxOccurs=”unbounded” we are not able to get single object as an Array. Is that ok if we use json:Array=’true’ at element declaration?

      Reply

Submit a Comment

Your email address will not be published. Required fields are marked *