Zawiń wszystkie cyfry w JSON cudzysłowami

11

Istnieją dane JSON, które zawierają pewne wartości liczbowe. Jak przekonwertować wszystkie cyfry na ciągi? (zawiń cytaty)

Przykład:

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

powinno stać się

{
        "id":"1",
        "customer":"user",
        "plate":"BMT-216-A",
        "country":"GB",
        "amount":"1000",
        "pndNumber":"20000",
        "zoneNumber":"4"
}
VK
źródło

Odpowiedzi:

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

Przekieruj do nowego pliku, a następnie przenieś go do oryginalnej nazwy pliku.

Rozważ dokładniejszą konwersję liczb w strukturach niepłaskich na ciągi znaków

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

Spowoduje to sprawdzenie rekurencyjnie każdej wartości w danym dokumencie i wybranie tych, które są liczbami. Wybrane wartości są następnie konwertowane na ciągi. Mówiłby też, ściśle mówiąc, o klawiszach, ale ponieważ w JSON nie mogą to być zwykłe liczby, żaden klucz nie zostałby wybrany.

Przykład:

$ 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"
  ]
}

Aby dodatkowo zacytować null, zmień select()na

select(type == "number" or type == "null")
Kusalananda
źródło
3
Pamiętaj, że zmienia się {"a":{"b":1},"b":null}na{ "a": "{\"b\":1}", "b": "null" }
Stéphane Chazelas
@ StéphaneChazelas Tak, zmieniłby podobiekty w ciągi znaków. Podana struktura danych nie zawiera jednak podobiektów.
Kusalananda
2
Nie tylko podobiekty, wszystkie wartości, w tym tablice, logiczne i null(wciąż warte odnotowania IMO, mimo że próbka PO nie ma żadnej z nich).
Stéphane Chazelas
A jak to zmienić, jeśli mam tablicę?
VK
@ StéphaneChazelas Sorted. Dzięki za szturchanie mnie.
Kusalananda
8

oto proste rozwiązanie oparte na jtcnarzędziu unix:

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 $ 

jeśli chcesz zastosować zmiany bezpośrednio w pliku json, użyj -fprzełącznika w następujący sposób:

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

Proponowane rozwiązanie będzie działać poprawnie z dowolną strukturą jsons, np .:

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 $ 
  • jeśli chcesz podać wartości zerowe, po prostu dodaj ścieżkę -w'<>n:'
  • jeśli chcesz zacytować wartości boolowskie, dodaj ścieżkę -w'<any>b:'

Również zadanie odwrotne (bez cytowania wszystkich liczb) można łatwo osiągnąć w podobny sposób: powiedzmy, że file.jsonjest już „przywołane”, aby cofnąć cytację wszystkich liczb:

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 $ 

AKTUALIZACJA : najnowsza wersja jtcimplementuje teraz szablony i przestrzenie nazw. Dzięki temu nie jest wymagane wywoływanie powłoki zewnętrznej:

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

jtcinstrukcja obsługi: https://github.com/ldn-softdev/jtc/blob/master/User%20Guide.md

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

Cytuję wszystko, co nie jest cytowane i nie jest []{}:,whitespace, więc cytuje liczby true, falsei null.

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

W szczególności zacytowałbym to, co pasuje do specyfikacji numeru json, a to nie jest już w cudzysłowie.

Wykonują one dokładne tokenizowanie w oparciu o specyfikację JSON, nie jest to przybliżenie.

Stéphane Chazelas
źródło
-1

Próbowałem z poniższą metodą i działało dobrze.

Dwa razy próbowałem potokować do mojego poziomu, aby go zmniejszyć

Komenda:

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

Wynik:

 {
        "id":"1",
        "customer":"user",
        "plate":"BMT-216-A",
        "country":"GB",
        "amount":"1000",
        "pndNumber":"20000",
        "zoneNumber":"4"
}
Praveen Kumar BS
źródło
@Kusalananda poprawił kod
Praveen Kumar BS
dlaczego używacie \{1,\},? Aby sprawdzić, czy element pojawia się co najmniej raz, użyj +. I to nie zadziała dla liczb takich jak -123, 0xab, 0o12, 0b1011, 1e23 lub 1.2e3 ...
phuclv
@phuclv \{1,\}jest BRE odpowiednikiem ERE +. Niektóre sedimplementacje obsługują \+jako przedłużenie lub -Elub -ropcję włączenia Eres, ale to nie jest przenośne. \?jest innym nieprzenośnym rozszerzeniem, którego standardowym odpowiednikiem jest\{0,1\}
Stéphane Chazelas
@ phuclv nie znajdziesz niecytowanych liczb 0xab 0o12 0b1011 w prawidłowym pliku JSON.
Stéphane Chazelas