Processing an API’s JSON response

Posted on

Problem

I am using the JSON output of the MediaWiki API documented here and here, and I have found that for Boolean values, it often returns an empty string if true and omits it if false. Thus, I want to “rationalize” the response.

(Historical context: This was before a new output format was added that uses native JSON true and false.)

My current code to do so (written some months ago) is located in a giant anonymous function:

user = query.users[0];
invalid = typeof user.invalid != "undefined";
missing = typeof user.missing != "undefined";
groups = (typeof user.groups == "object") ? user.groups : [];
editcount = (typeof user.editcount == "number") ? user.editcount : null;
registration = (typeof user.registration == "string") ?
    UserinfoJsParseDate(user.registration) : null;
blocked = typeof user.blockedby != "undefined";
gender = (typeof user.gender == "string") ? user.gender : null;
lastEdited = (typeof query.usercontribs[0] == "object") &&
    (typeof query.usercontribs[0].timestamp == "string") ?
    UserinfoJsParseDate(query.usercontribs[0].timestamp) : null;

I am trying to clean this up, breaking the script’s main components into separate functions:

/**
 * Extract relevant information from the server's response.
 * @param data The server's response to the AJAX request
 * @return An object containing the user's information
 */
function processResponse( data ) {
    var query = data.query, info = {};

    if ( query && query.users && query.users[0] ) {
        var user = query.users[0];

        info.invalid = 'invalid' in user;
        info.missing = 'missing' in user;
        info.groups = user.groups || [];
        info.editcount = ( 'editcount' in user ) ? user.editcount : null;
        info.registration = ( 'registration' in user ) ? parseDate( user.registration ) : null;
        info.blocked = 'blockexpiry' in user;
        info.gender = ( 'gender' in user && user.gender != 'unknown' ) ? user.gender : null;

        info.lastEdited = null;
        if ( query.usercontribs && query.usercontribs[0] ) {
            var contribs = query.usercontribs[0];
            if ( contribs.timestamp ) {
                user.lastEdited = parseDate( contribs.timestamp );
            }
        }

    }

    return info;
}

Is this better, or have I just written more messy code?

Solution

It is certainly better. You can improve it further:

  1. Use early exits to reduce arrow code.
  2. Use a JSON initializer for the result.
  3. Prefix boolean names with ‘is’.
  4. Fix the ‘user.lastEdited’ bug.

.

function processResponse(data) {
    if (!data.query || !data.query.users || !data.query.users[0]) return {};

    var user = data.query.users[0];

    return {
        isInvalid: 'invalid' in user,
        isMissing: 'missing' in user,
        groups: user.groups || [],
        editCount: ('editcount' in user) ? user.editcount : null,
        registration: ('registration' in user) ? parseDate(user.registration) : null,
        isBlocked: 'blockexpiry' in user,
        gender: ('gender' in user && user.gender != 'unknown') ? user.gender : null,
        lastEdited: (data.query.usercontrib
                             && data.query.usercontribs[0] 
                             && data.query.usercontribs[0].timestamp)
                        ? parseDate(data.query.usercontribs[0].timestamp) : null
    };
}

Leave a Reply

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