frenchie
frenchie

Reputation: 51937

javascript JSON parse object property directly to int

I have some objects that I'm parsing from json using native browser implementations. Some of the objects' properties are numbers. For the moment, the numbers are parse from json as strings and I use parseInt to cast the string to the int I need.

The problem is that I've got 23 objects I do this with and overall about 80 properties that I'm parsing to ints like this:

if (TheObject && TheObject.TheProperty) {
   TheObject.TheProperty = parseInt(TheObject.TheProperty, 10);
}

There are many lines of code that look very similar. Is there a way using prototypes or something to change the way the JSON.parse function works so that each time the parser runs it checks to see if a string property is actually an int and if so cast it directly as such?

Thanks.

Upvotes: 13

Views: 41412

Answers (5)

T.J. Crowder
T.J. Crowder

Reputation: 1074238

If your data source can't be fixed (numbers should be passed as numbers, not as strings), you can pass JSON.parse a "reviver" function, which will receive each item as it's being processed. This gives you the option of transforming it:

// Create this once
var propsToConvert = {
    TheProperty: 1,
    TheOtherProperty: 1,
    YetAnotherProperty: 1,
    // ...and so on...
};

// Use it each time you parse
var obj = JSON.parse(str, function(key, value) {
    if (propsToConvert.hasOwnProperty(key)) {
        return parseInt(value, 10);
    }
    return value;
});

Live example | source

Or if the property names are not sufficiently unique (TheProperty doesn't always need handling, just when it's a property of TheObject), you can do this as a two-level check:

// Define the object names and their property names (once)
var propsToConvert = {
    TheObject: {
        TheProperty: 1,
        TheOtherProperty: 1,
        YetAnotherProperty: 1,
        // ...and so on...
    },
    AnotherObject: {
        // Other properties...
    }
};

// Use it each time you parse
var obj = JSON.parse(str, function(key, value) {
    var name, props;

    if (typeof value === "object") {
        props = propsToConvert[key];
        if (props) {
            for (name in props) {
                value[name] = parseInt(value[name], 10);
            }
        }
    }
});

(Revivers are called inside-out, so the properties will be on the object by the time you see the object's key; that's why we update them in place.)

You get the idea, there's a lot you can do with reviver functions.


Side note: parseInt, which I've used above, is fairly forgiving — possibly more forgiving than you want. For instance:

var a = parseInt('1a', 10); // 1, instead of NaN

If you're okay with strings like "0x10" being treated as hex, then:

var a = Number(str);

...which will give you NaN for invalid number strings (Number("1a") is NaN). Since JSON isn't meant to have hex numbers, if you're sure the broken data source won't encode them as hex, you're golden.

Otherwise, if you need decimal but you want to be strict, you'll need to do a regex on the string to make sure it matches the pattern for a valid decimal number (which is fairly complex, if you want to support all the stuff JavaScript numeric literals support).

Upvotes: 4

cliffs of insanity
cliffs of insanity

Reputation: 3694

JSON.parse accepts a second argument in the form of a function that can do some post processing.

JSON.parse('{"p": "5"}', function(k, v) { 
    return (typeof v === "object" || isNaN(v)) ? v : parseInt(v, 10); 
});

I you don't want to process all numeric strings, then create a lookup table of the properties you do want.

var props = {"p":1, "some_prop":1, "another_prop":1};

JSON.parse('{"p": "5"}', function(k, v) { 
    return props.hasOwnProperty(k) ? parseInt(v, 10) : v; 
});

Upvotes: 14

Amadan
Amadan

Reputation: 198324

@kbec gives the correct answer. If you don't have control over your data source, then you can use something like this:

function intify(obj, fields) {
  if (typeof(obj) == "undefined") return;
  var numFields = fields.length;
  for (var i = 0; i < numFields; i++) {
    var field = fields[i];
    if (typeof(obj[field]) != "undefined") {
      obj[field] = parseInt(obj[field], 10);
    }
  }
  return obj;
}

intify(obj, ['foo', 'bar']);
intify(obj.baz, ['boo']);

Upvotes: 1

Michael Berkowski
Michael Berkowski

Reputation: 270607

I don't think you'll be able to modify the parser, but instead of many similar lines of code, use an array of the properties and access them with the [] notation in a loop to parseInt() them. Of course, if you have access to the code producing the JSON, it is easier to change it to output ints properly unquoted.

// Array of properties you want to parse out
var parseUs = ['prop1','prop2','prop3','prop4'];

// Array of objects you need to parse them in
var objs = [obj1, obj2, obj3];

// Iterate over the objects
for (var i=0; i<objs.length; i++) {

  // And over the properties array
  for (var j=0; j<parseUs.length; j++) {
    // Parse out the int value if the object has the property
    if (objs[i].hasOwnProperty(parseUs[j]) {
      objs[i][parseUs[j]] = parseInt(parseUs[j], 10);
    }
  }
}

Note: This won't work if the objects share property names which are not int values in all of them. If that's the case, you would need to modify this to use an array of properties per object.

Upvotes: 2

kbec
kbec

Reputation: 3465

JSON can handle numbers as follow:

{
    "TheObject":{
        "TheProperty":5
    }
}

If your property was doublequoted then it's a string else it's a numeric, boolean (true and false values), null or just something that cause parse error.

See http://json.org/

Upvotes: 9

Related Questions