2

I have object in json string:

const str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`;

I want to parse this string (JSON.parse), but I also want to convert every underscore key to camelCase (in all levels).

Is it possible to use parse and convert in the same function? or I need to do it in separate steps?

Jon Sud
  • 10,211
  • 17
  • 76
  • 174

5 Answers5

1

Because you asked in the comment how to do that with reviver, here a solution using that method.

But I'm not sure if I should consider that one as a good solution.

It relies on how the traversing for the reviving pass is done and according to 25.5.1 JSON.parse ( text [ , reviver ] ) and 25.5.1.1 InternalizeJSONProperty ( holder, name, reviver ) it should always work.

But I personally wouldn't rely on that (because reviver wasn't intended for that usage) and instead, reimplement the traversing.

this in the reviver callback refers to the container to which key and value belong.

const str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`

const snakeToCamel = str => str.replace(/([-_]\w)/g, g => g[1].toUpperCase());

var parsed = JSON.parse(str, function(key, value) {
  const camelCaseKey = snakeToCamel(key)
 
  if (this instanceof Array || camelCaseKey === key) {
    // if this is Array 
    // or key does not change after converted to camel case
    // then just return the value so that the default "reviving" is done
    return value
  } else {
    // if key changes assing value to camel case one and return nothing
    this[camelCaseKey] = value
  }
});

console.dir(parsed)

source of snakeToPascal

t.niese
  • 39,256
  • 9
  • 74
  • 101
  • That's fun, I've tried this solution earlier in the TS playground and it doesn't seem to work... Works on JSFiddle though. Maybe this unintended use case isn't supported in some versions, I don't know... – deb Jul 12 '21 at 06:46
  • @a2br the code works fine for me on TS playground in Firefox and Chromium-based browsers. In addition to that TS playground is for typescript, so if it does not work there but here and on JSFiddle then the problem is likely to be the TS-Compiler or the settings. – t.niese Jul 12 '21 at 08:57
0

I just made a playground that solves that problem. You can check it out here, that was a fun problem.

const str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`;
const obj = JSON.parse(str);

function toCamelCase(snake_case) {
  return snake_case.replace(/([-_][a-z])/ig, ($1) => {
    return $1.toUpperCase()
      .replace('-', '')
      .replace('_', '');
  });
}

function transformObject(obj) {
  if (obj && Array.isArray(obj)) {
    return obj.map(e => transformObject(e));
  }
  if (obj && typeof obj === "object") {
    const final = {};
    for (const [key, val] of Object.entries(obj)) {
      final[toCamelCase(key)] = transformObject(val);
    }
    return final;
  }
  return obj;
}
const transformed = transformObject(obj);
console.log(transformed);
deb
  • 6,671
  • 2
  • 11
  • 27
0

Here is something I think should work:

const str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`;
let obj = JSON.parse(str);


function snakeToCamel(str) {
    const splitString = str.split('_');
    let result = "";
    splitString.forEach((sstr, iter) => {
        if (iter !== 0) {
            result += sstr.charAt(0).toUpperCase() + sstr.slice(1);
        } else
            result += sstr;

    })
    return result;
}

function convertToCamelCase(target) {
    const keys = Object.keys(target);
    keys.forEach(key => {
        if (typeof target[key] === 'object')
            convertToCamelCase(target[key]);

        const converted = snakeToCamel(key);
        if (key !== converted) {
            target[converted] = target[key];
            delete target[key];
        }
    });
}

convertToCamelCase(obj)
console.log(obj);
Marko Borković
  • 1,884
  • 1
  • 7
  • 22
0

A simple Array.reduce() function with a recursive camelCase modifier function will do the trick. This is scalable and works for deeply nested variables as well

const str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`;

const getCamelCaseFrom = (a) => {
  return Object.fromEntries(Object.entries(a).map(s => {
    if (Array.isArray(s[1])) s[1] = s[1].map(x => getCamelCaseFrom(x));
    s[0] = s[0].split('_').map((e, i) => i === 0 ? e : e.slice(0, 1).toUpperCase() + e.slice(1)).join('');
    return s
  }))
}
let json = JSON.parse(str).reduce((b, a) => ([...b, getCamelCaseFrom(a)]), [])

console.log(json)
Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • I don't know if the code as it is now can be properly used, as `str` could very well be a JSON object, not an array – deb Jul 12 '21 at 06:52
  • this is the OP's data and it is a JSON object, not sure what you mean – Kinglish Jul 12 '21 at 07:03
  • The answer's completely fine for that input data, what I meant was it wouldn't work on something that isn't an array – deb Jul 12 '21 at 07:04
0

With no regex and by simple reducing you may perhaps do like;

function snake2camel(str){
  var b = false;
  return Array.prototype.reduce.call( str
                                    , (r,c) => c === "_" ? ( b = true
                                                           , r
                                                           )
                                                         : r += (b ? ( b = false
                                                                     , c.toUpperCase()
                                                                     )
                                                                   : c)
                                    , ""
                                    );
}
var str = `[{"user_id":"561904e8-6e45-5012-a9d8-e2ff8761acf6","email_addr":"jilov@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]},{"user_id":"5904003b-452b-535c-9615-94706bf6c66c","email_addr":"sawu@fake.com","details":[{"city_name":"fake city","country_name":"fake country"}]}]`;

console.log(JSON.parse(snake2camel(str)));
.as-console-wrapper {
  max-height: 100% !important;
}
Redu
  • 25,060
  • 6
  • 56
  • 76