【问题标题】:Convert returned JSON Object Properties to (lower first) camelCase将返回的 JSON 对象属性转换为(较低的第一个)camelCase
【发布时间】:2012-10-07 13:32:11
【问题描述】:

我从这样的 API 返回 JSON:

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

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

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

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

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

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

【问题讨论】:

  • 如果你真的很想在 javascript 中使用驼峰式表示法,你将不得不映射传入的对象。 api.jquery.com/jQuery.map 应该可以帮助您进行映射。

标签: javascript jquery json lodash underscore.js


【解决方案1】:

如果您使用lodash 代替下划线,则可以这样做:

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

这会将TitleCasesnake_case 都转换为camelCase。请注意,它不是递归的。

【讨论】:

  • 注意:这不适用于嵌套属性,仅适用于顶级键。
【解决方案2】:

这是一个可靠的递归函数,可以正确地对 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
    }
}

【讨论】:

  • 我建议检查o.hasOwnProperty(key) 并将if (typeof value === "object") 更改为if (value !== null && typeof value === "object")。如果不检查value !== null,此函数会将空值转换为空对象。
  • 好的提示 - 将添加这些提示。
  • 如果 o 是 Date 对象(或任何没有自己属性的对象),这将丢失值信息。结帐演示codepen.io/anon/pen/EPqZLY?editors=1010
  • 一年后,我终于花时间正确检测非普通对象属性(如Date)。我们检查构造函数是否为Object,然后再对其余部分进行驼峰式封装。
  • @brandonscript 如果任何属性是一个数组,它实际上会丢失,你只检查主对象是否是一个数组。此行缺少检查它是否为数组:if (value !== null && value.constructor === Object) 这是您失败的示例:plnkr.co/edit/tEBYU3pQmJJZyl84CFo5 修复了检查数组:plnkr.co/edit/78gDwcLChAOT31XGoLG6
【解决方案3】:

你可以用这个递归函数来做到这一点(使用 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
   }
}

【讨论】:

  • 完全符合我的需要。谢谢。
  • 非常感谢。工作正常。我会用_.isPlainObject(obj)替换obj !== null && obj.constructor === Object
  • 完美转换嵌套对象(包括数组和诸如此类),这里不能说其他一些答案。 +1
  • 这似乎是唯一适用于所有情况的答案
  • 这应该是公认的答案——最新的
【解决方案4】:

要将普通对象的键从 snake_case 更改为 camelCase 递归请尝试以下操作
(使用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中测试

注意:也递归地作用于数组中的对象

【讨论】:

  • @Kody - 仅为您更新代码和 plunker - 现在适用于嵌套在数组中的对象(JSON 或其他):-)
  • plunker 真的为我节省了很多时间。
  • 我将 _.isPlainObject(value) 更改为 _.isObject(value),它对我有用,谢谢!
  • 注意,如果你传递给它,这会展平一个数组 - 并返回一个对象
  • 正如@GalBracha 所说,这似乎不会将数组保留为数组。我一起破解了这个:plnkr.co/edit/OnLVNqq7dHW1T3ukuyd1
【解决方案5】:

使用 lodash 和 ES6,这会将所有键递归替换为驼峰式:

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) 
    };
  }, {});
};      

【讨论】:

  • 我最喜欢的解决方案
  • 如果我们有这样的对象,这将失败: [{ hello: 0 }, { hello: null }, { hello: undefined }, { hello: false }] 它会返回 [{hello: {}}, {hello: {}}, {hello: {}), {hello: {}}] 可以通过添加 if (obj === 0 || obj === null || obj === 来修复未定义 || obj === false) { 返回 obj; }
  • 很好,速记版本与扩展版本不一致。我已更新为仅显示扩展答案
【解决方案6】:

这是axios interceptors 的一个很好的用例

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

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

这是一个gist,其中包含一个使用 React/axios 的更长示例。

【讨论】:

  • IMO - 这可能是最好的答案,使用拦截器是一种非常干净的方法,可以将所有 snake_case 键移动到 camelCase 并将代码保存在一个健全的地方,tyvm
【解决方案7】:

只用驼峰

humps.camelize('hello_world');
humps.camelizeKeys(object, options); // will work through entire object

https://www.npmjs.com/package/humps

【讨论】:

  • 很棒的图书馆推荐
【解决方案8】:

使用 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 };
  }, {});
};

【讨论】:

    【解决方案9】:

    有一个很好的 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。在反应框架内对我来说很好,感谢您的回答!
    【解决方案10】:

    好吧,我接受了挑战,并认为我想通了:

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

    Property converter (jsfiddle)

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

    【讨论】:

    • 这是一个不错的解决方案!两个警报都是大写的,但我明白了。整件事困扰着我,如果我这样做并且不更改它,它会弄乱我所有的模型/控制器。它只是一种痛苦。谢谢。
    • 现在是驼峰式的属性名称,请注意映射中的.givenName 和映射中的.GivenName...确认属性已更改名称而不是值。跨度>
    • 这是一个设计决策,这是拥有动态语言的难点。对自己应用某些约定以保持一切正常:)
    【解决方案11】:

    以下是您可能想尝试的方便库: https://www.npmjs.com/package/camelize2

    您只需使用npm install --save camelize2 安装它,然后

    const camelize = require('camelize2')
    
    const response = {
       Contacts: [{ GivenName: "Matt", FamilyName:"Berry" }]
    }
    
    const camelizedResponse = camelize(response)
    

    【讨论】:

      【解决方案12】:

      这个解决方案基于上面的纯js解决方案,使用loadash如果作为参数传递则保留一个数组并且只改变Keys强>

      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>

      【讨论】:

      • 该函数只在处理数组时是递归的。一旦遇到非数组对象,它只会对顶级属性键进行驼峰式处理。
      • 这不是纯 JS 解决方案。这需要 lodash 所以从技术上讲它不是纯 JS...
      【解决方案13】:

      我需要一个接受数组或对象的通用方法。这就是我正在使用的(我借用了 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);}以确保数组的值也可以被映射
      • 好点。数组有效,但非数组对象无效。我编辑以支持这些。
      【解决方案14】:

      通过 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 };
        }, {});
      };
      

      【讨论】:

        【解决方案15】:

        使用 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
        }

        【讨论】:

          【解决方案16】:

          类似于@brandonscript 的解决方案,但采用更多 ES6 功能的方式:

          const camelCaseString = str => (
            (str.charAt(0).toLowerCase() + str.slice(1) || str).toString() 
          );
          
          const objectToCamelCase = val => {
            if (typeof val != 'object' || val === null) {
              return val;
            }
           
            if (val instanceof Array) {
              return val.map(objectToCamelCase);
            }
           
            return Object.keys(val)
              .filter(prop => val.hasOwnProperty(prop))
              .map(prop => ({[camelCaseString(prop)]: objectToCamelCase(val[prop])}))
              .reduce((prev, current) => ({...prev, ...current}))
          };
          
          // Example:
          let converted = objectToCamelCase({UserId: 1, Hobbies: [{Id: 1, Label: "Read"}], Name: "John Doe"});
          
          console.log(converted)

          【讨论】:

            【解决方案17】:

            使用来自https://plnkr.co/edit/jtsRo9yU12geH7fkQ0WL?p=preview 的引用更新了代码 这通过将数组保持为数组(您可以使用 map 对其进行迭代)来处理带有数组的对象以及其中的对象等等。

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

            【讨论】:

              【解决方案18】:

              这是我的看法;比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 定义的属性。
              【解决方案19】:

              以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
              }
              

              【讨论】:

                【解决方案20】:

                这是我找到的代码,虽然没有经过全面测试,但值得分享。 它比其他答案更具可读性,不确定性能。

                测试它http://jsfiddle.net/ms734bqn/1/

                const toCamel = (s) => {
                      return s.replace(/([-_][a-z])/ig, ($1) => {
                        return $1.toUpperCase()
                          .replace('-', '')
                          .replace('_', '');
                      });
                    };
                
                const isArray = function (a) {
                  return Array.isArray(a);
                };
                
                const isObject = function (o) {
                  return o === Object(o) && !isArray(o) && typeof o !== 'function';
                };
                
                const keysToCamel = function (o) {
                  if (isObject(o)) {
                    const n = {};
                
                    Object.keys(o)
                      .forEach((k) => {
                        n[toCamel(k)] = keysToCamel(o[k]);
                      });
                
                    return n;
                  } else if (isArray(o)) {
                    return o.map((i) => {
                      return keysToCamel(i);
                    });
                  }
                
                  return o;
                };
                

                【讨论】:

                  【解决方案21】:

                  将对象键转换为深的驼峰式。

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

                  【讨论】:

                    猜你喜欢
                    • 2021-03-05
                    • 1970-01-01
                    • 2018-09-25
                    • 2017-11-03
                    • 1970-01-01
                    • 2014-11-12
                    • 2020-02-02
                    • 1970-01-01
                    • 2018-10-14
                    相关资源
                    最近更新 更多