13

我有一些使用本机浏览器实现从 json 解析的对象。一些对象的属性是数字。目前,数字从 json 解析为字符串,我使用 parseInt 将字符串转换为我需要的 int。

问题是我有 23 个对象我这样做了,总共有大约 80 个属性我正在解析为这样的整数:

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

有许多看起来非常相似的代码行。有没有办法使用原型或其他东西来改变 JSON.parse 函数的工作方式,以便每次解析器运行时检查一个字符串属性是否实际上是一个 int ,如果是这样直接转换它?

谢谢。

4

5 回答 5

14

JSON.parse接受可以进行一些后处理的函数形式的第二个参数。

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

我不想处理所有数字字符串,然后创建您想要的属性的查找表。

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; 
});
于 2012-05-12T13:00:33.860 回答
9

JSON 可以按如下方式处理数字:

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

如果您的属性是双引号,那么它是一个字符串,否则它是一个数字、布尔值(truefalse值),null或者只是导致解析错误的东西。

http://json.org/

于 2012-05-12T12:49:49.740 回答
4

如果您的数据源无法固定(数字应作为数字传递,而不是作为字符串传递),您可以传递JSON.parse一个“reviver”函数,它将接收正在处理的每个项目。这为您提供了转换它的选项:

// 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;
});

实例| 资源

或者,如果属性名称不够独特(TheProperty并不总是需要处理,只是当它是 的属性时TheObject),您可以将其作为两级检查来执行:

// 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 被称为由内而外,所以当你看到对象的键时,属性将在对象上;这就是我们更新它们的原因。)

你明白了,你可以用 reviver 功能做很多事情。


旁注:parseInt,我在上面使用过,是相当宽容的——可能比你想要的更宽容。例如:

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

如果您可以接受像"0x10"被视为十六进制的字符串,那么:

var a = Number(str);

...这将为您NaN提供无效的数字字符串(Number("1a")is NaN)。由于 JSON 并不意味着具有十六进制数字,因此如果您确定损坏的数据源不会将它们编码为十六进制,那么您就是黄金。

否则,如果您需要小数但又想严格,则需要对字符串执行正则表达式以确保它与有效十进制数的模式匹配(如果您想支持所有内容,这相当复杂JavaScript 数字文字支持)。

于 2012-05-12T13:01:14.480 回答
2

我认为您无法修改解析器,但不要使用许多类似的代码行,而是使用属性数组并使用[]循环中的符号访问parseInt()它们。当然,如果您可以访问生成 JSON 的代码,则更容易将其更改为正确输出不带引号的整数。

// 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);
    }
  }
}

注意:如果对象共享的属性名称不是所有的 int 值,这将不起作用。如果是这种情况,您需要修改它以使用每个对象的属性数组。

于 2012-05-12T12:50:21.147 回答
1

@kbec 给出了正确的答案。如果您无法控制数据源,则可以使用以下内容:

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']);
于 2012-05-12T12:53:06.653 回答