将返回的JSON对象属性转换为(最低优先级)camelCase


74

我有从API返回的JSON,如下所示:

Contacts: [{ GivenName: "Matt", FamilyName: "Berry" }]

为了使其与我的代码风格(camelCase-小写首字母)保持一致,我想对数组进行转换以产生以下内容:

 contacts: [{ givenName: "Matt", familyName: "Berry" }]

最简单/最好的方法是什么?创建一个新的Contact对象并遍历返回数组中的所有联系人?

var jsonContacts = json["Contacts"],
    contacts= [];
        
_.each(jsonContacts , function(item){
    var contact = new Contact( item.GivenName, item.FamilyName );
    contacts.push(contact);
});

还是可以映射原始数组或以某种方式对其进行转换?


1
如果您真的很想在JavaScript中使用驼峰符号,则必须映射传入的对象。api.jquery.com/jQuery.map应该可以帮助您进行映射。
KyorCode

Answers:


86

这是一个可靠的递归函数,它将适当地驼峰化所有JavaScript对象的属性:

function toCamel(o) {
  var newO, origKey, newKey, value
  if (o instanceof Array) {
    return o.map(function(value) {
        if (typeof value === "object") {
          value = toCamel(value)
        }
        return value
    })
  } else {
    newO = {}
    for (origKey in o) {
      if (o.hasOwnProperty(origKey)) {
        newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString()
        value = o[origKey]
        if (value instanceof Array || (value !== null && value.constructor === Object)) {
          value = toCamel(value)
        }
        newO[newKey] = value
      }
    }
  }
  return newO
}

测试:

var obj = {
  'FirstName': 'John',
  'LastName': 'Smith',
  'BirthDate': new Date(),
  'ArrayTest': ['one', 'TWO', 3],
  'ThisKey': {
    'This-Sub-Key': 42
  }
}

console.log(JSON.stringify(toCamel(obj)))

输出:

{
    "firstName":"John",
    "lastName":"Smith",
    "birthDate":"2017-02-13T19:02:09.708Z",
    "arrayTest": [
        "one", 
        "TWO", 
        3
    ],
    "thisKey":{
        "this-Sub-Key":42
    }
}

1
我建议检查o.hasOwnProperty(key)并更改if (typeof value === "object")if (value !== null && typeof value === "object")。如果不检查value !== null此函数,则将null转换为空对象。
user619656

好的建议-将添加这些。
brandonscript

1
如果o是Date对象(或任何不具有自己属性的Object),则这将丢失值信息。签出演示codecode.io/anon/pen/EPqZLY?editors=1010
tsobe

一年后,我终于花了一些时间来正确检测非平原对象的属性(例如Date)。我们检查构造函数是否是Object驼峰式的,然后将其其余部分包装起来。
brandonscript

1
@brandonscript实际上会错过任何属性是否为数组的情况,您只会检查主对象是否为数组。此行缺少检查是否为数组的检查: if (value !== null && value.constructor === Object) 这是您失败的示例: plnkr.co/edit/tEBYU3pQmJJZyl84CFo5 修复了检查数组的问题:plnkr.co/edit/78gDwcLChAOT31XGoLG6
Justin

82

如果使用lodash而不是下划线,则可以这样做:

_.mapKeys(obj, (v, k) => _.camelCase(k))

这会将TitleCase和都转换snake_casecamelCase。注意,它不是递归的。


完善。应该是公认的答案。
DotBot

33

您可以使用以下递归函数(使用lodash和ES6)执行此操作:

import { camelCase } from 'lodash';

const camelizeKeys = (obj) => {
  if (Array.isArray(obj)) {
    return obj.map(v => camelizeKeys(v));
  } else if (obj !== null && obj.constructor === Object) {
    return Object.keys(obj).reduce(
      (result, key) => ({
        ...result,
        [camelCase(key)]: camelizeKeys(obj[key]),
      }),
      {},
    );
  }
  return obj;
};

测试:

const obj = {
  'FirstName': 'John',
  'LastName': 'Smith',
  'BirthDate': new Date(),
  'ArrayTest': ['one', 'TWO', 3],
  'ThisKey': {
    'This-Sub-Key': 42
  }
}

console.log(JSON.stringify(camelizeKeys(obj)))

输出:

{  
   "firstName": "John",
   "lastName": "Smith",
   "birthDate": "2018-05-31T09:03:57.844Z",
   "arrayTest":[  
      "one",
      "TWO",
      3
   ],
   "thisKey":{  
      "thisSubKey": 42
   }
}

正是我需要的。谢谢。
Nidhi Shah,

1
非常感谢。工作良好。我将替换 obj !== null && obj.constructor === Object_.isPlainObject(obj)
dimaqw

1
完美地转换一个嵌套对象(包括数组和诸如此类的东西),这里不能说其他一些答案。+1
fgblomqvist

1
这似乎是唯一适用于所有情况的答案
Emmanuel NK

1
这应该是公认的答案-最最新
fidev

20

要将普通对象的键从更改snake_casecamelCase 递归,请尝试以下操作
(使用Lodash):

function objectKeysToCamelCase(snake_case_object) {
  var camelCaseObject = {};
  _.forEach(
    snake_case_object,
    function(value, key) {
      if (_.isPlainObject(value) || _.isArray(value)) {     // checks that a value is a plain object or an array - for recursive key conversion
        value = objectKeysToCamelCase(value);               // recursively update keys of any values that are also objects
      }
      camelCaseObject[_.camelCase(key)] = value;
    }
  )
  return camelCaseObject;
};

在此PLUNKER中测试

注意:也可以递归地处理数组中的对象


1
@Kody-代码和插件已为您更新-现在可用于嵌套在数组中的对象(JSON或其他方式:-))
goredwards

1
矮子确实节省了我很多时间。
khichar.anil

1
我将_.isPlainObject(value)更改为_.isObject(value),对我有用,谢谢!

5
请注意,如果您传递给它这削弱了阵列-并返回一个对象,而不是
半乳糖的Bracha

2
正如@GalBracha所说,这似乎并不将数组保持为数组。:我砍死了一起,而不是plnkr.co/edit/OnLVNqq7dHW1T3ukuyd1
ropeladder

7

使用lodash和ES6,这将以递归方式将所有键替换为camelcase:

速记:

const camelCaseKeys = (obj) => ((!_.isObject(obj) && obj) || (_.isArray(obj) && obj.map((v) => camelCaseKeys(v))) || _.reduce(obj, (r, v, k) => ({ ...r, [_.camelCase(k)]: camelCaseKeys(v) }), {}));

展开:

const camelCaseKeys = (obj) => {
  if (!_.isObject(obj)) {
    return obj;
  } else if (_.isArray(obj)) {
    return obj.map((v) => camelCaseKeys(v));
  }
  return _.reduce(obj, (r, v, k) => {
    return { 
      ...r, 
      [_.camelCase(k)]: camelCaseKeys(v) 
    };
  }, {});
};      

我最喜欢的解决方案
garrettmac

3
如果我们有这样的对象,它将失败:[{hello:0},{hello:null},{hello:undefined},{hello:false}],它将返回[{hello:{}},{hello:{ }},{hello:{}),{hello:{}}]可以通过添加if(obj === 0 || obj === null || obj === undefined || obj === false来修复){return obj; }
mikkeljuhl

4

这是axios拦截器的好用例

基本上,定义一个客户端类,并附加一个转换请求/响应数据的之前/之后拦截器。

export default class Client {
    get(url, data, successCB, catchCB) {
        return this._perform('get', url, data, successCB, catchCB);
    }

    post(url, data, successCB, catchCB) {
        return this._perform('post', url, data, successCB, catchCB);
    }

    _perform(method, url, data, successCB, catchCB) {
        // https://github.com/axios/axios#interceptors
        // Add a response interceptor
        axios.interceptors.response.use((response) => {
            response.data = toCamelCase(response.data);
            return response;
        }, (error) => {
            error.data = toCamelCase(error.data);
            return Promise.reject(error);
        });

        // Add a request interceptor
        axios.interceptors.request.use((config) => {
            config.data = toSnakeCase(config.data);
            return config;
        }, (error) => {
            return Promise.reject(error);
        });

        return axios({
            method: method,
            url: API_URL + url,
            data: data,
            headers: {
                'Content-Type': 'application/json',
            },
        }).then(successCB).catch(catchCB)
    }
}

这是一个使用React / axios的较长示例的要点


3

有这个一个不错的NPM模块.. https://www.npmjs.com/package/camelcase-keys

npm install camelcase-keys
const camelcaseKeys = require( "camelcase-keys" );

camelcaseKeys( { Contacts: [ { GivenName: "Matt", FamilyName: "Berry" } ] }, { deep: true } );

将返回...

{ contacts: [ { givenName: "Matt", familyName: "Berry" } ] }

我特别喜欢这个答案,因为整个程序包约为9kb。在react框架中对我来说效果很好,谢谢您的回答!
Jason R Stevens CFA

2

好吧,我接受了挑战,认为自己已经解决了:

var firstToLower = function(str) {
    return str.charAt(0).toLowerCase() + str.slice(1);
};

var firstToUpper = function(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
};

var mapToJsObject = function(o) {
    var r = {};
    $.map(o, function(item, index) {
        r[firstToLower(index)] = o[index];
    });
    return r;
};

var mapFromJsObject = function(o) {
    var r = {};
    $.map(o, function(item, index) {
        r[firstToUpper(index)] = o[index];
    });
    return r;
};


// Map to
var contacts = [
    {
        GivenName: "Matt",
        FamilyName: "Berry"
    },
    {
        GivenName: "Josh",
        FamilyName: "Berry"
    },
    {
        GivenName: "Thomas",
        FamilyName: "Berry"
    }
];

var mappedContacts = [];

$.map(contacts, function(item) {
    var m = mapToJsObject(item);
    mappedContacts.push(m);
});

alert(mappedContacts[0].givenName);


// Map from
var unmappedContacts = [];

$.map(mappedContacts, function(item) {
    var m = mapFromJsObject(item);
    unmappedContacts.push(m);
});

alert(unmappedContacts[0].GivenName);

属性转换器(jsfiddle)

诀窍是将对象作为对象属性的数组进行处理。


1
多数民众赞成在一个不错的解决方案!两种警报都以大写形式出现,但我明白了。整个问题困扰着我,如果我这样做了,或者如果我不更改它,就会搞乱我所有的模型/控制器。这只是一个痛苦。谢谢。
乔恩·威尔斯

现在是驼峰式中的属性名称,请注意.givenNamein映射到map和.GivenNamefrom中的映射...确认属性已更改名称而不是值。
KyorCode

1
这是一个设计决策,这是使用动态语言的难点。对自己套用某些约定,以保持一切
顺畅

2

使用lodash,您可以这样做:

export const toCamelCase = obj => {
  return _.reduce(obj, (result, value, key) => {
    const finalValue = _.isPlainObject(value) || _.isArray(value) ? toCamelCase(value) : value;
    return { ...result, [_.camelCase(key)]: finalValue };
  }, {});
};

1

此解决方案基于上面的普通js解决方案,使用loadash并保留数组(如果作为参数传递),并且仅更改

function camelCaseObject(o) {
    let newO, origKey, value
    if (o instanceof Array) {
        newO = []
        for (origKey in o) {
            value = o[origKey]
            if (typeof value === 'object') {
                value = camelCaseObject(value)
            }
            newO.push(value)
        }
    } else {
        newO = {}
        for (origKey in o) {
            if (o.hasOwnProperty(origKey)) {
                newO[_.camelCase(origKey)] = o[origKey]
            }
        }
    }
    return newO
}

// Example
const obj = [
{'my_key': 'value'},
 {'Another_Key':'anotherValue'},
 {'array_key':
   [{'me_too':2}]
  }
]
console.log(camelCaseObject(obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>


1
该函数仅在处理数组时是递归的。一旦碰到了非数组对象,它只会驼峰使用顶级属性键。
扎里菲斯


0

我需要一个接受数组或对象的通用方法。这就是我正在使用的(我借用了KyorCode的firstToLower()实现):

function convertKeysToCamelCase(obj) {
    if (!obj || typeof obj !== "object") return null;

    if (obj instanceof Array) {
        return $.map(obj, function(value) {
            return convertKeysToCamelCase(value);
        });
    }

    var newObj = {};
    $.each(obj, function(key, value) {
        key = key.charAt(0).toLowerCase() + key.slice(1);
        if (typeof value == "object" && !(value instanceof Array)) {
          value = convertKeysToCamelCase(value);
        }
        newObj[key] = value;
    });

    return newObj;
};

示例调用:

var contact = { GivenName: "Matt", FamilyName:"Berry" };

console.log(convertKeysToCamelCase(contact));
// logs: Object { givenName="Matt", familyName="Berry"}

console.log(convertKeysToCamelCase([contact]));
// logs: [Object { givenName="Matt", familyName="Berry"}]

console.log(convertKeysToCamelCase("string"));
// logs: null

console.log(contact);
// logs: Object { GivenName="Matt", FamilyName="Berry"}

$.each循环内部,您应该添加内容if (value instanceof Array) {value = convertKeysToCamelCase(value);}以确保也可以映射作为数组的值
Norrec

好点子。数组有效,但非数组对象无效。我编辑以支持这些。
欧文

0

使用参考从更新的代码https://plnkr.co/edit/jtsRo9yU12geH7fkQ0WL?p=preview 此提手阵列对象与它里面的物体太等,通过保持数组作为阵列(其可以使用地图遍历)

function snakeToCamelCase(snake_case_object){
  var camelCaseObject;
  if (isPlainObject(snake_case_object)) {        
    camelCaseObject = {};
  }else if(isArray(snake_case_object)){
    camelCaseObject = [];
  }
  forEach(
    snake_case_object,
    function(value, key) {
      if (isPlainObject(value) || isArray(value)) {
        value = snakeToCamelCase(value);
      }
      if (isPlainObject(camelCaseObject)) {        
        camelCaseObject[camelCase(key)] = value;
      }else if(isArray(camelCaseObject)){
        camelCaseObject.push(value);
      }
    }
  )
  return camelCaseObject;  
}

0

使用lodash和某些es6 +功能应对挑战这是我使用reduce函数的实现。

function deeplyToCamelCase(obj) {
  return _.reduce(obj, (camelCaseObj, value, key) => {
    const convertedDeepValue = _.isPlainObject(value) || _.isArray(value)
      ? deeplyToCamelCase(value)
      : value;
    return { ...camelCaseObj, [_.camelCase(key)] : convertedDeepValue };
  }, {});
};

0

这是我的看法;比brandoncode的实现更具可读性,嵌套更少,并且有更大的空间来处理诸如Date(未得到处理)或的边缘情况null

function convertPropertiesToCamelCase(instance) {
    if (instance instanceof Array) {
        var result = [];

        for (var i = 0; i < instance.length; i++) {
            result[i] = convertPropertiesToCamelCase(instance[i]);
        }

        return result;
    }

    if (typeof instance != 'object') {
        return instance;
    }

    var result = {};

    for (var key in instance) {
        if (!instance.hasOwnProperty(key)) {
            continue;
        }

        result[key.charAt(0).toLowerCase() + key.substring(1)] = convertPropertiesToCamelCase(instance[key]);
    }

    return result;
}

很好,但是当转换为TypeScript时,它将删除使用getter和setter定义的属性。
扎里菲斯

0

建立在goredwards答案上(无法正确处理数组字段)

function objectKeysToCamelCase(snake_case_object) {
  let camelCaseObject = {}
  _.forEach(
    snake_case_object,
    function(value, key) {
      if (_.isPlainObject(value)) {
        value = objectKeysToCamelCase(value)
      } else if (_.isArray(value)) {
        value = value.map(v => _.isPlainObject(v) ? objectKeysToCamelCase(v) : v)
      }
      camelCaseObject[_.camelCase(key)] = value
    },
  )
  return camelCaseObject
}

0

使用lodash ...

function isPrimitive (variable) {
  return Object(variable) !== variable
}

function toCamel (variable) {
  if (isPrimitive(variable)) {
    return variable
  }

  if (_.isArray(variable)) {
    return variable.map(el => toCamel(el))
  }

  const newObj = {}
  _.forOwn(variable, (value, key) => newObj[_.camelCase(key)] = toCamel(value))

  return newObj
}


-2

用深转换对象键为camelCase。

import _ from 'lodash';

export function objectKeysToCamelCase(entity) {
    if (!_.isObject(entity)) return entity;

    let result;

    result = _.mapKeys(entity, (value, key) => _.camelCase(key));
    result = _.mapValues(result, (value) => objectKeysToCamelCase(value));

    return result;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.