1

I've got a JSON object whose keys have dots in their names. I need to replace the dots with 'DOT' (for example).

    {
        "key1.key": "merciful",
        "key2": {
            "key2.key": "grateful"
        }
    }

So, key1.key converts to key1DOTkey

Using the approach suggested in Change key name in nested JSON structure I used the reviver parameter of JSON.Parse, which works like a charm to replace anything within the key name, except for dots: when replacing dots, it truncates the object.

This code replaces all "e" and works fine

var parseE = JSON.parse(obj, function (k, v) {
    if (k.search(".") != -1)
       this[k.replace(/e/g, 'DOT')] = v;
    else
        return v;
});

returns

{
    "kDOTy1.kDOTy": "merciful",
    "kDOTy2": {
        "kDOTy2.kDOTy": "grateful"
    }
}

But if I try to replace the dot ".", then the object truncates

var parseDOT = JSON.parse(obj, function (k, v) {
    if (k.search(".") != -1)
       this[k.replace(/\./g, 'DOT')] = v;
    else
        return v;
});

returns the first key-value pair, well replaced, but nothing else:

{
    "key1DOTkey": "merciful"
}

I have tried using replaceAll and even creating a function replacing the characters one by one to avoid using regex in case that was the origin. Nothing, same outcome.

Here's a fiddle: https://jsfiddle.net/dpiret/dgk5fp16/7/

Note: replacing all dots from the stringified object won't work for me because it would replace the dots within values as well.

I would much appreciate any indication

daniel p
  • 741
  • 7
  • 12

2 Answers2

2

search uses regular expressions (so . matches any character!), just use string methods like indexOf

var obj = `{
    "key1.key": "merciful",
    "key2": {
        "key2.key": "grateful"
    },
    "key3": {
        "key3.keyA.keyB": "thankful"
    }
}`
var parseDOT = JSON.parse(obj, function (k, v) {
    let key = k;
    if (key.indexOf(".") != -1){
       while(key.indexOf(".") != -1)
          key = key.replace(".","DOT");
       this[key] = v;
    }
    else
        return v;
});
console.log(parseDOT)

I'm sorry that I'm struggling to explain why the above works. What I can tell you is your original solution matched every key - what is not clear is why it didnt just update every property with a no-op for those without a dot.

Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • It would be helpful to explain the OP why exactly `parse` fails in combination with `search`. – georg Jun 25 '21 at 10:08
  • @georg you're right, it would. I'm just a bit baffled. I came to the solution before I came to the reasoning why. – Jamiec Jun 25 '21 at 10:12
  • 1
    yep, it took me a bit of thinking too. I guess the reason is, they return `undefined` from the first branch, thus telling the parser to remove the key `k`. Since `key2.replace(/./. DOT)` is still `key2`, the assignment is a no-op and the key is removed. Yet another javascript wtf moment... – georg Jun 25 '21 at 10:18
  • @georg I'm about 2 minutes behind you, think ive just wrapped my head round what was happening – Jamiec Jun 25 '21 at 10:20
  • Thanks @Jamiec. Your solution works for the first dot, not when it has multiple (which is my case), like `"key1.key.ke": "merciful"` which results in `"key1DOTkey.ke":"merciful"}`. ReplaceAll doesn't work in Node 14. I also tried the old way, which truncates the object as well: ```javascript const replaceChars=(string, replaced, replacer)=>{ var newString=''; for (var c=0;c – daniel p Jun 27 '21 at 20:05
  • @danielp [my update](https://stackoverflow.com/a/68129190/295783) handles multiple dots – mplungjan Jun 28 '21 at 05:10
  • 1
    @danielp see updated answer - you can simply use a `while` to keep replacing dots until there are no more in lieu `replaceAll` (I'm fairly sure thats all `replaceAll` does under the hood!) – Jamiec Jun 28 '21 at 08:26
  • Thanks, @Jamiec. Your solution works, and it does so within the parameters of using the `reviver` function. @mplungjan 's works fine too, although it uses a different approach ignoring JSON.Parse() – daniel p Jun 30 '21 at 09:52
1

Replace will work - this one does not touch the values

const str = `{
    "key1.key": "merciful",
    "key2": {
        "key2.key": "grateful.dead"
    },
    "key3": {
        "key3.key.key": "text.text"
    }
}`
const str1 = str.replace(/"(\w+\.\w+)+":/g,function(match) { return match.replace(/\./g,"DOT")})
const obj = JSON.parse(str1);
console.log(obj);

Checking "text.text": as value

const str = `{
    "key1.key": "merciful",
    "key2": {
        "key2.key": "grateful.dead"
    },
    "key3": {
        "key3.key.key": "text.text"
    }
}`

const obj = JSON.parse(str);
console.log(obj);
obj["key3"]["key3.key.key"] = `"text.text":`

console.log(obj);

let str1 = JSON.stringify(obj)

console.log(str1);

str1 = str1.replace(/"(\w+\.\w+)+":/g,function(match) { return match.replace(/\./g,"DOT")})

console.log(str1);

const obj1 = JSON.parse(str1)

console.log(obj1)
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • what if the dot is not in a property name? – Guerric P Jun 25 '21 at 10:02
  • Thanks, @mplungjan. This works. I was trying to avoid this approach of identifying key names based on the quotes and colon, but I guess it works 99% of the cases, which is when there are no pieces like `"text":` within the value. Another option I considered is doing a deep copy of the object using a recursive function, replacing the name where needed. But it seems a bit of an overkill and performance matters here. – daniel p Jun 28 '21 at 05:58
  • 1
    The JSON string will NOT have any "text": in the values. They will be escaped and look like `"\"text\":"` and not be picked up by my regex - see test I wrote – mplungjan Jun 28 '21 at 06:42
  • You are right, @mplungjan. Using your solution and works just fine. Thanks! – daniel p Jun 29 '21 at 06:18