9

There are JSON data which contains some numeric values. How to convert all numerics to strings? (wrap with quotes)

Example:

{
        "id":1,
        "customer":"user",
        "plate":"BMT-216-A",
        "country":"GB",
        "amount":1000,
        "pndNumber":20000,
        "zoneNumber":4
}

should become

{
        "id":"1",
        "customer":"user",
        "plate":"BMT-216-A",
        "country":"GB",
        "amount":"1000",
        "pndNumber":"20000",
        "zoneNumber":"4"
}
28
$ jq 'map_values(tostring)' file.json
{
  "id": "1",
  "customer": "user",
  "plate": "BMT-216-A",
  "country": "GB",
  "amount": "1000",
  "pndNumber": "20000",
  "zoneNumber": "4"
}

Redirect to a new file and then move that to the original filename.

For a more thorough conversion of numbers in non-flat structures into strings, consider

jq '(..|select(type == "number")) |= tostring' file.json

This would examine every value recursively in the given document, and select the ones that are numbers. The selected values are then converted into strings. It would also, strictly speaking, look at the keys, but since these can't be plain numbers in JSON, no key would be selected.

Example:

$ jq . file.json
{
  "a": {
    "b": 1
  },
  "b": null,
  "c": [
    1,
    2,
    "hello",
    4
  ]
}
$ jq '(..|select(type == "number")) |= tostring' file.json
{
  "a": {
    "b": "1"
  },
  "b": null,
  "c": [
    "1",
    "2",
    "hello",
    "4"
  ]
}

To additionally quote the null, change the select() to

select(type == "number" or type == "null")
  • 3
    Note that it changes {"a":{"b":1},"b":null} to { "a": "{\"b\":1}", "b": "null" } – Stéphane Chazelas Mar 5 at 10:19
  • @StéphaneChazelas Yes, it would turn sub-objects into strings. The given data structure does not however contains sub-objects. – Kusalananda Mar 5 at 10:21
  • 2
    Not only sub-objects, all values including arrays, booleans and null (still worth noting IMO even though the OP's sample has none of those). – Stéphane Chazelas Mar 5 at 10:23
  • And how to change this if I have an array? – V-K Mar 5 at 10:24
  • @StéphaneChazelas Sorted. Thanks for poking at me. – Kusalananda Mar 5 at 10:31
8

here's an easy solution based on jtc unix utility:

bash $ jtc -w'<.*>D:' -eu echo '"{}"' \; file.json
{
   "amount": "1000",
   "country": "GB",
   "customer": "user",
   "id": "1",
   "plate": "BMT-216-A",
   "pndNumber": "20000",
   "zoneNumber": "4"
}
bash $ 

if you like to apply changes right into the json file, use the -f switch, like this:

bash $ jtc -f -w'<.*>D:' -eu echo '"{}"' \; file.json

The proposed solution will work correctly with an arbitrary structured jsons, e.g.:

bash $ jtc -w'<.*>D:' -eu echo '"{}"' \; file.json
{
   "amount": "1000",
   "country": "GB",
   "customer": "user",
   "id": "1",
   "plate": "BMT-216-A",
   "pndNumber": "20000",
   "sub": {
      "subvalue": "123"
   },
   "zoneNumber": "4"
}
bash $ 
  • if you like to quote null values, just throw in a walk-path -w'<>n:'
  • if you like to quote boolean values, throw in a walk-path -w'<any>b:'

Also, the reverse task (unquote all the numerics) is easily achieved in the similar way: say, file.json is already "enquoted", to unquote all the numerics:

bash $ jtc -w'<^\d+$>R:' -eu echo {-} \; file.json
{
   "amount": 1000,
   "country": "GB",
   "customer": "user",
   "id": 1,
   "plate": "BMT-216-A",
   "pndNumber": 20000,
   "zoneNumber": 4
}
bash $ 

UPDATE: the latest version of jtc implements now templates and namespaces. With that no invocation of external shell is required:

bash $ jtc -w'<.*>D:' -u'<.*>D:<val>v' -T'"{val}"' file.json
{
   "amount": "1000",
   "country": "GB",
   "customer": "user",
   "id": "1",
   "plate": "BMT-216-A",
   "pndNumber": "20000",
   "zoneNumber": "4"
}

jtc user guide: https://github.com/ldn-softdev/jtc/blob/master/User%20Guide.md

4
perl -pe 's/("(?:\\.|[^"])*")|[^\s[\]{}:,"]+/$1||qq("$&")/ge' file.json

Would quote anything that is not quoted and is not []{}:,whitespace, so would quote numbers, true, false and null.

perl -pe 's/("(?:\\.|[^"])*")|-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/$1||qq("$&")/ge'

Would specifically quote what matches the specification of a json number and that is not already inside quotes.

Those do an exact tokenising based on the JSON specification, it's not an approximation.

-1

I tried with below method and it worked fine.

I pipelined 2 times tried up to my level to reduce it

Command:

sed 's/[0-9]\{1,\},\?$/"&/g' filename |
sed '/[0-9]\{1,\}$/s/[0-9]\{1,\}/&"/g'|
sed '/[0-9]\{1,\},$/s/,$/"&/g`'

Output:

 {
        "id":"1",
        "customer":"user",
        "plate":"BMT-216-A",
        "country":"GB",
        "amount":"1000",
        "pndNumber":"20000",
        "zoneNumber":"4"
}
  • @Kusalananda corrected the code – Praveen Kumar BS Mar 5 at 16:51
  • why do you use \{1,\},? To test whether an element appears one or more times use +. And this won't work for numbers like -123, 0xab, 0o12, 0b1011, 1e23 or 1.2e3... – phuclv Mar 6 at 6:21
  • @phuclv \{1,\} is the BRE equivalent of ERE +. Some sed implementations support \+ as an extension or a -E or -r option to enable EREs but that's not portable. \? is another non-portable extension though whose standard equivalent is \{0,1\} – Stéphane Chazelas Mar 6 at 7:27
  • @phuclv you wouldn't find unquoted 0xab 0o12 0b1011 numbers in a valid JSON file. – Stéphane Chazelas Mar 6 at 7:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.