Jak sprawdzić, czy ciąg znaków jest poprawny JSON w Pythonie?

184

Czy w Pythonie istnieje sposób, aby sprawdzić, czy ciąg jest prawidłowy JSON przed próbą jego przetworzenia?

Na przykład praca z takimi funkcjami, jak Facebook Graph API, czasami zwraca JSON, czasem może zwrócić plik obrazu.

Joey Blake
źródło
3
interfejs API powinien ustawić typ zawartości
John La Rooy
4
Nie możesz określić, jakie dane są zwracane w wywołaniu API? Nie znam interfejsu API Facebooka, ale brzmi to naprawdę dziwnie.
jhocking
Zrobiłem to raz, ale w sposób codegolf
TY
1
Większość odpowiedzi to json, ale jeśli nazwiesz zdjęcie profilowe, to po prostu zwróci jpg
Joey Blake

Odpowiedzi:

234

Możesz spróbować zrobić json.loads(), co spowoduje wyrzucenie a, ValueErrorjeśli przekazany ciąg nie może zostać zdekodowany jako JSON.

Zasadniczo filozofia „ pytoniczna ” dla tego rodzaju sytuacji nazywa się EAFP , ponieważ łatwiej prosić o przebaczenie niż pozwolenie .

John Flatness
źródło
4
Widzę, jak to będzie działać. Prowadzi mnie do następnego pytania. Zgłasza błąd ValueError. W tym momencie chcę, aby zwrócił ciąg obrażający, aby mógł zrobić z nim coś innego. Do tej pory otrzymałem tylko komunikat o błędzie i typ.
Joey Blake,
2
Co jest złego w zwróceniu ciągu, który przekazałeś loadsw klauzuli wyjątku?
John Flatness,
1
nie ma w tym nic złego, po prostu błąd noob z mojej strony. Wygląda na to, że nie mogę dwa razy wywołać file.read (). Ale mogę ustawić zmienną i użyć jej. I to właśnie zrobiłem.
Joey Blake,
5
tylko uwaga ... json.loads ('10 ') nie rzuca ValueError i jestem pewien, że' 10 'nie jest prawidłowym jsonem ...
wahrheit
4
Pomimo tego, że specyfikacja mówi, że tekst JSON musi być tablicą lub obiektem, większość koderów i dekoderów (w tym Pythona) będzie działać z dowolną wartością JSON na „górze”, włączając liczby i ciągi znaków. 10jest prawidłową wartością liczbową JSON.
John Flatness,
145

Przykładowy skrypt Python zwraca wartość logiczną, jeśli ciąg jest prawidłowy json:

import json

def is_json(myjson):
  try:
    json_object = json.loads(myjson)
  except ValueError as e:
    return False
  return True

Które wydruki:

print is_json("{}")                          #prints True
print is_json("{asdf}")                      #prints False
print is_json('{ "age":100}')                #prints True
print is_json("{'age':100 }")                #prints False
print is_json("{\"age\":100 }")              #prints True
print is_json('{"age":100 }')                #prints True
print is_json('{"foo":[5,6.8],"foo":"bar"}') #prints True

Konwertuj ciąg JSON na słownik Python:

import json
mydict = json.loads('{"foo":"bar"}')
print(mydict['foo'])    #prints bar

mylist = json.loads("[5,6,7]")
print(mylist)
[5, 6, 7]

Konwertuj obiekt python na ciąg JSON:

foo = {}
foo['gummy'] = 'bear'
print(json.dumps(foo))           #prints {"gummy": "bear"}

Jeśli chcesz uzyskać dostęp do analizowania niskiego poziomu, nie twórz własnych, użyj istniejącej biblioteki: http://www.json.org/

Świetny samouczek na temat modułu Python JSON: https://pymotw.com/2/json/

Jest String JSON i pokazuje błędy składniowe i komunikaty o błędach:

sudo cpan JSON::XS
echo '{"foo":[5,6.8],"foo":"bar" bar}' > myjson.json
json_xs -t none < myjson.json

Wydruki:

, or } expected while parsing object/hash, at character offset 28 (before "bar}
at /usr/local/bin/json_xs line 183, <STDIN> line 1.

json_xs jest zdolny do sprawdzania składni, parsowania, czyszczenia, kodowania, dekodowania i innych:

https://metacpan.org/pod/json_xs

Eric Leschinski
źródło
Czy uważasz, że powinniśmy del json_objectraz zatwierdzić?
Akshay
4
Dlaczego do diabła nie ma odpowiedniej metody sprawdzania poprawności? Powinien istnieć sposób sprawdzania błędów bez zabijania kanarków.
Braden Best
Chodzi mi o to: tylko dlatego, że Python pozwala na OO, nie oznacza, że ​​można ignorować inne części. Powinienem mieć opcję albo: A. pozwolenie na awarię funkcji i użycie wyjątków (sposób OO / Python), lub B. wywołanie funkcji, która zwraca wartość (sukces lub błąd) zamiast zgłaszania wyjątku, a następnie włączenie mojej funkcji , z kolei zwraca wartość wartownika wskazującą na błąd, dzięki czemu błędy rozbijają stos wywołań i mogą być używane w razie potrzeby (procedura / C). Tak jak C ++ nie zmusza cię do korzystania z wyjątków (możesz użyć errno), Python nie powinien tego też wymuszać
Braden Best
Sprawdzanie poprawności ciągu @BradenBest JSON jest nawiedzane przez demona, który sprawia, że ​​problem zatrzymania jest interesujący. Nie ma matematycznie poprawnego sposobu udowodnienia poprawności łańcucha, prócz wypróbowania go przy pomocy parsera i sprawdzenia, czy kończy się bez błędów. Aby zobaczyć, dlaczego jest to trudne: „Napisz do mnie program, który udowodni, że nie ma błędów składniowych w programie komputerowym”. To nie jest możliwe. Twórcy języków będą poetycko opowiadać o wiecznym wyścigu zbrojeń w zakresie kodowania i dekodowania. Najlepsze, co możemy zrobić, to zwrócić tak / nie, jeśli łańcuch jest prawidłowy dla danego silnika, a nie dla wszystkich możliwych silników.
Eric Leschinski,
1
@EricLeschinski, ale tutaj nie ma problemu z zatrzymaniem. Program wyraźnie zgłasza wyjątek, jeśli wystąpi błąd podczas analizowania JSON. Dlatego program wie, kiedy dane wejściowe JSON są nieprawidłowe. Dlatego w 100% możliwe jest posiadanie funkcji sprawdzającej, czy dane wejściowe są prawidłowe bez konieczności ich używania try. #StopCanaryAbuse
Braden Best
2

Powiedziałbym, że parsowanie to jedyny sposób, w jaki można naprawdę całkowicie powiedzieć. Wyjątkiem będzie json.loads()funkcja pytona (prawie na pewno), jeśli nie jest to poprawny format. Jednak w celach twojego przykładu możesz prawdopodobnie po prostu sprawdzić pierwszą parę znaków spoza ...

Nie znam JSON, który Facebook wysyła z powrotem, ale większość ciągów JSON z aplikacji internetowych rozpocznie się od otwartego nawiasu kwadratowego [lub kręconego {. Znane mi formaty obrazów nie zaczynają się od tych znaków.

I odwrotnie, jeśli wiesz, jakie formaty obrazu mogą się wyświetlać, możesz sprawdzić początek ciągu pod kątem ich podpisów w celu identyfikacji obrazów i założyć, że masz JSON, jeśli nie jest to obraz.

Innym prostym hackiem do identyfikacji grafiki, a nie ciągu tekstowego, w przypadku gdy szukasz grafiki, jest po prostu przetestowanie znaków spoza ASCII w pierwszych kilkudziesięciu znakach łańcucha (zakładając, że JSON to ASCII ).

Tim
źródło
0

Wymyśliłem ogólne, interesujące rozwiązanie tego problemu:

class SafeInvocator(object):
    def __init__(self, module):
        self._module = module

    def _safe(self, func):
        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except:
                return None

        return inner

    def __getattr__(self, item):
        obj = getattr(self.module, item)
        return self._safe(obj) if hasattr(obj, '__call__') else obj

i możesz go używać w następujący sposób:

safe_json = SafeInvocator(json)
text = "{'foo':'bar'}"
item = safe_json.loads(text)
if item:
    # do something
odedlaz
źródło
1
Myślę, że ogólne rozwiązania są dobre, ale w tym przypadku exceptklauzula może ukrywać każdy poważny wyjątek. Łapanie wyjątków musi być jak najbardziej restrykcyjne.
lucastamoios