Reputation: 51937
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
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;
});
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
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
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
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
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