27

I have an object like

{ "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }

I want to convert it to dot notation (one level) version like:

{ "status": "success", "auth.code": "23123213", "auth.name": "qwerty asdfgh" }

Currently I am converting the object by hand using fields but I think there should be a better and more generic way to do this. Is there any?

Note: There are some examples showing the opposite way, but i couldn't find the exact method.

Note 2: I want it for to use with my serverside controller action binding.

8
  • 1
    parse the JSON string ... iterata over it's subitems and after that convert it back to JSON string Commented Nov 4, 2012 at 13:04
  • 1
    @Elias Van Ootegem: It's not a valid identifier, but every string is a valid property name. Commented Nov 4, 2012 at 13:08
  • after parsing, you can access code by author.code because auth is an Object and code is a property of this Object Commented Nov 4, 2012 at 13:08
  • My question is why? There must be a JSON implementation for your favorite server language Commented Nov 4, 2012 at 13:09
  • @mplungjan Actually I am using C#, and I want to use the posted object with an actual object, not a dictionary. If I directly post the first object, it postes like "auth[code]". That's why. Thanks Commented Nov 4, 2012 at 13:36

9 Answers 9

41

You can recursively add the properties to a new object, and then convert to JSON:

var res = {};
(function recurse(obj, current) {
  for(var key in obj) {
    var value = obj[key];
    var newKey = (current ? current + "." + key : key);  // joined key with dot
    if(value && typeof value === "object") {
      recurse(value, newKey);  // it's a nested object, so do it again
    } else {
      res[newKey] = value;  // it's not an object, so set the property
    }
  }
})(obj);
var result = JSON.stringify(res);  // convert result to JSON
Sign up to request clarification or add additional context in comments.

3 Comments

I wrote another function with prefix feature. I couldn't run your code but I got the answer. Thanks
for future reference: the library, @vardars is mentioning can is linked below (stackoverflow.com/a/13218902/2602592) or can directly be found at github.com/vardars/dotize
this does not work if there is a Date field since type of Date is object, but it has no keys. Similarly, the way it format arrays may not be as intended. For obj = { inner: {a: 6,s: 'muhammed'}, arr: [1, 3], now: new Date(), }; it gives {"inner.a":6,"inner.s":"muhammed","arr.0":1,"arr.1":3}
11

Here is a fix/hack for when you get undefined for the first prefix. (I did)

var dotize = dotize || {};

dotize.parse = function(jsonobj, prefix) {
  var newobj = {};
  function recurse(o, p) {
    for (var f in o)
    {
      var pre = (p === undefined ? '' : p + ".");
      if (o[f] && typeof o[f] === "object"){
        newobj = recurse(o[f], pre + f);
      } else {
        newobj[pre + f] = o[f];
      }
    }
    return newobj;
  }
  return recurse(jsonobj, prefix);
};

Comments

9

You can use the NPM dot-object (Github) for transform to object to dot notation and vice-versa.

var dot = require('dot-object'); 
var obj = {
  id: 'my-id',
  nes: { ted: { value: true } },
  other: { nested: { stuff: 5 } },
  some: { array: ['A', 'B'] }
};

var tgt = dot.dot(obj);

Produces

{
  "id": "my-id",
  "nes.ted.value": true,
  "other.nested.stuff": 5,
  "some.array[0]": "A",
  "some.array[1]": "B"
}

Comments

2

const sourceObj = { "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }
;

const { auth, ...newObj } = sourceObj;

const resultObj = {
  ...newObj,
  ..._.mapKeys(auth, (val, key) => `auth.${key}`)
}


// function approach
const dotizeField = (obj, key) => {
  const { ...newObj } = sourceObj;

  delete newObj[key];

  return {
    ...newObj,
    ..._.mapKeys(obj[key], (val, subKey) => `${key}.${subKey}`)
  }
}

const resultObj2 = dotizeField(sourceObj, 'auth');

console.log(sourceObj, resultObj, resultObj2);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Comments

2

i think this would be more elegant...

const toDotNot = (input, parentKey) => Object.keys(input || {}).reduce((acc, key) => {
  const value = input[key];
  const outputKey = parentKey ? `${parentKey}.${key}` : `${key}`;

  // NOTE: remove `&& (!Array.isArray(value) || value.length)` to exclude empty arrays from the output
  if (value && typeof value === 'object' && (!Array.isArray(value) || value.length)) return ({ ...acc, ...toDotNot(value, outputKey) });

  return ({ ...acc, [outputKey]: value });
}, {});

const input = {a: {b: 'c', e: {f: ['g', null, {g: 'h'}]}}, d: []};
const output = toDotNot(input);

console.log(output);

results in:

// output:
{
    "a.b": "c",
    "a.e.f.0": "g",
    "a.e.f.1": null,
    "a.e.f.2.g": "h",
    "d": []
}

Comments

2

i have done some fix:

export function toDotNotation(obj,res={}, current='') {
  for(const key in obj) {
    let value = obj[key];
    let newKey = (current ? current + "." + key : key);  // joined key with dot
    if(value && typeof value === "object") {
      toDotNotation(value,res, newKey);  // it's a nested object, so do it again
    } else {
      res[newKey] = value;  // it's not an object, so set the property
    }
  }
  return res;
}


Comments

2

There are already lots of answers here, but for Typescript this solution works pretty well for me and is typed:

type EncapsulatedStringObject =  Record<string, string | object>;
export function convertKeysToDotNotation( object: EncapsulatedStringObject, prefix: string = '' ): Record<string, string> {
    const result: Record<string, string> = {};
    Object.keys( object ).forEach( key => {
        const newPrefix = prefix ? `${prefix}.${key}` : key;
        const value = object[ key ];
        if ( typeof value === 'object' ) {
            Object.assign( result, convertKeysToDotNotation( object[ key ] as EncapsulatedStringObject, newPrefix ) );
        } else {
            result[ newPrefix ] = value;
        }
    } );
    return result;
}

Comments

1

I wrote another function with prefix feature. I couldn't run your code but I got the answer. Thanks

https://github.com/vardars/dotize

var dotize = dotize || {};

dotize.convert = function(jsonobj, prefix) {
    var newobj = {};

    function recurse(o, p, isArrayItem) {
        for (var f in o) {
            if (o[f] && typeof o[f] === "object") {
                if (Array.isArray(o[f]))
                    newobj = recurse(o[f], (p ? p + "." : "") + f, true); // array
                else {
                    if (isArrayItem)
                        newobj = recurse(o[f], (p ? p : "") + "[" + f + "]"); // array item object
                    else
                        newobj = recurse(o[f], (p ? p + "." : "") + f); // object
                }
            } else {
                if (isArrayItem)
                    newobj[p + "[" + f + "]"] = o[f]; // array item primitive
                else
                    newobj[p + "." + f] = o[f]; // primitive
            }
        }
        return newobj;
    }

    return recurse(jsonobj, prefix);
};

3 Comments

If it works, then that's fine, but this code requires each property name to have at least one dot in it. That's not the case for success.
yes I realized that, and tried to fix with using global var. :)
github library is updated, has some tests. I can't regularly update this answer, sorry.
1

Following what @pimvdb did (a compact and effective solution he submitted), I added a little modification that allows me have a function that can be easily exported:

    function changeObjectToDotNotationFormat(inputObject, current, prefinalObject) {
      const result = prefinalObject ? prefinalObject : {}; // This allows us to use the most recent result object in the recursive call
      for (let key in inputObject) {
        let value = inputObject[key];
        let newKey = current ? `${current}.${key}` : key;
        if (value && typeof value === "object") {
          changeObjectToDotNotationFormat(value, newKey, result);
        } else {
          result[newKey] = value;
        }
      }
      return result;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.